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

#include "chrome/browser/ash/cert_provisioning/cert_provisioning_worker_dynamic.h"

#include <stdint.h>

#include <memory>
#include <string>
#include <vector>

#include "base/base64.h"
#include "base/compiler_specific.h"
#include "base/containers/flat_map.h"
#include "base/functional/callback.h"
#include "base/json/json_string_value_serializer.h"
#include "base/json/json_writer.h"
#include "base/memory/raw_ptr.h"
#include "base/no_destructor.h"
#include "base/strings/stringprintf.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_future.h"
#include "base/test/values_test_util.h"
#include "base/time/time.h"
#include "chrome/browser/ash/attestation/mock_tpm_challenge_key_subtle.h"
#include "chrome/browser/ash/attestation/tpm_challenge_key_subtle.h"
#include "chrome/browser/ash/cert_provisioning/cert_provisioning_common.h"
#include "chrome/browser/ash/cert_provisioning/cert_provisioning_metrics.h"
#include "chrome/browser/ash/cert_provisioning/cert_provisioning_test_helpers.h"
#include "chrome/browser/ash/cert_provisioning/mock_cert_provisioning_client.h"
#include "chrome/browser/ash/cert_provisioning/mock_cert_provisioning_invalidator.h"
#include "chrome/browser/ash/platform_keys/key_permissions/fake_user_private_token_kpm_service.h"
#include "chrome/browser/ash/platform_keys/key_permissions/key_permissions_manager.h"
#include "chrome/browser/ash/platform_keys/key_permissions/key_permissions_manager_impl.h"
#include "chrome/browser/ash/platform_keys/key_permissions/mock_key_permissions_manager.h"
#include "chrome/browser/ash/platform_keys/key_permissions/user_private_token_kpm_service_factory.h"
#include "chrome/browser/ash/platform_keys/mock_platform_keys_service.h"
#include "chrome/browser/ash/platform_keys/platform_keys_service.h"
#include "chrome/browser/ash/platform_keys/platform_keys_service_factory.h"
#include "chromeos/ash/components/dbus/attestation/fake_attestation_client.h"
#include "chromeos/ash/components/platform_keys/platform_keys.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash::cert_provisioning {
namespace {

namespace em = ::enterprise_management;

using attestation::MockTpmChallengeKeySubtle;
using ::base::test::IsJson;
using ::base::test::ParseJsonDict;
using ::base::test::RunOnceCallback;
using ::chromeos::platform_keys::HashAlgorithm;
using ::chromeos::platform_keys::KeyAttributeType;
using ::chromeos::platform_keys::Status;
using ::chromeos::platform_keys::TokenId;
using platform_keys::KeyUsage;
using ::testing::_;
using ::testing::AtLeast;
using ::testing::Eq;
using ::testing::Mock;
using ::testing::SaveArg;
using ::testing::StrictMock;

// Generated by chrome/test/data/policy/test_certs/create_test_certs.sh
constexpr char kFakeRsaCertificate[] = R"(-----BEGIN CERTIFICATE-----
MIIDJzCCAg+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxyb290
X2NhX2NlcnQwHhcNMjAwMjI1MTUyNTU2WhcNMzAwMjIyMTUyNTU2WjAUMRIwEAYD
VQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDW
druvpaJovmyWzIcjtsSk/lp319+zNPSYGLzJzTeEmnFoDf/b89ft6xR1NIahmvVd
UHGOMlzgDKnNkqWw+pgpn6U8dk+leWnwlUefzDz7OY8qXfX29Vh0m/kATQc64lnp
rX19fEi2DOgH6heCQDSaHI/KAnAXccwl8kdGuTEnvdzbdHqQq8pPGpEqzC/NOjk7
kDNkUt0J74ZVMm4+jhVOgZ35mFLtC+xjfycBgbnt8yfPOzmOMwXTjYDPNaIy32AZ
t66oIToteoW5Ilg+j5Mto3unBDHrw8rml3+W/nwHuOPEIgBqLQFfWtXpuX8CbcS6
SFNK4hxCJOvlzUbgTpsrAgMBAAGjgYAwfjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQW
BBRDEl1/2pL5LtKnpIly+XCj3N6MwDAfBgNVHSMEGDAWgBQrwVEnUQZlX850A2N+
URfS8BxoyzAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0RBAgw
BocEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAXZd+Ul7GUFZPLSiTZ618hUI2UdO0
7rtPwBw3TephWuyEeHht+WhzA3sRL3nprEiJqIg5w/Tlfz4dsObpSU3vKmDhLzAx
HJrN5vKdbEj9wyuhYSRJwvJka1ZOgPzhQcDQOp1SqonNxLx/sSMDR2UIDMBGzrkQ
sDkn58N5eWm+hZADOAKROHR47j85VcsmYGK7z2x479YzsyWyOm0dbACXv7/HvFkz
56KvgxRaPZQzQUg5yuXa21IjQz07wyWSYnHpm2duAbYFl6CTR9Rlj5vpRkKsQP1W
mMhGDBfgEskdbM+0agsZrJupoQMBUbD5gflcJlW3kwlboi3dTtiGixfYWw==
-----END CERTIFICATE-----)";

constexpr char kFakeEcCertificate[] = R"(-----BEGIN CERTIFICATE-----
MIIBnzCCAUSgAwIBAgIBATAKBggqhkjOPQQDAjAaMRgwFgYDVQQDDA9yb290X2Nh
X2NlcnRfZWMwHhcNMjQwOTEyMTA1MzU3WhcNMzQwOTEwMTA1MzU3WjAUMRIwEAYD
VQQDDAkxMjcuMC4wLjEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT/wC+O25T8
nNajXBE1z9NDXCapWyQxFFNBcW05P/aL9/iLYUituFbVeMOfOmLOj9nC5qzS2LqA
pAr3bYlWnGpzo4GAMH4wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUwJJZe59phGeQ
wgXyt1y0Ea5+zJ4wHwYDVR0jBBgwFoAU2WQdI/7A5otWqaRzXqM1DONaiQkwHQYD
VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEQQIMAaHBH8AAAEwCgYI
KoZIzj0EAwIDSQAwRgIhAIXaaFpXMig3AunQhkq/HhZ0YIwuKHC9yT05aonHisXV
AiEA9PFcoCoCvUKHghq5xfNaSI3B8e/UBDhSQHIRRsMAl0Q=
-----END CERTIFICATE-----)";

// Extracted from kFakeRsaCertificate using the command:
// openssl x509 -pubkey -noout -in cert.pem
// and reformatted as a single line.
constexpr char kPublicKeyRsaBase64[] =
    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1na7r6WiaL5slsyHI7bEpP5ad9ffsz"
    "T0mBi8yc03hJpxaA3/2/"
    "PX7esUdTSGoZr1XVBxjjJc4AypzZKlsPqYKZ+lPHZPpXlp8JVHn8w8+"
    "zmPKl319vVYdJv5AE0HOuJZ6a19fXxItgzoB+"
    "oXgkA0mhyPygJwF3HMJfJHRrkxJ73c23R6kKvKTxqRKswvzTo5O5AzZFLdCe+"
    "GVTJuPo4VToGd+ZhS7QvsY38nAYG57fMnzzs5jjMF042AzzWiMt9gGbeuqCE6LXqFuSJYPo+"
    "TLaN7pwQx68PK5pd/lv58B7jjxCIAai0BX1rV6bl/Am3EukhTSuIcQiTr5c1G4E6bKwIDAQAB";

// Extracted from kFakeEcCertificate using the command:
// openssl x509 -pubkey -noout -in cert.pem
// and reformatted as a single line.
constexpr char kPublicKeyEcBase64[] =
    "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/8AvjtuU/JzWo1wRNc/"
    "TQ1wmqVskMRRTQXFtOT/2i/f4i2FIrbhW1XjDnzpizo/Zwuas0ti6gKQK922JVpxqcw==";

// A certificate that doesn't match the public key kPublicKeyRsaBase64.
// Taken from net/data/ssl/certificates/client_1.pem
constexpr char kFakeRsaCertificatePubKeyMismatch[] =
    R"(-----BEGIN CERTIFICATE-----
MIIDEjCCAfqgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEQiBDq
QTAeFw0yMjEwMTkxNjU4NTVaFw0zMjEwMTYxNjU4NTVaMBgxFjAUBgNVBAMMDUNs
aWVudCBDZXJ0IEEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa+Dq7
TTFSw1AxRkaftrCM8tuPbYH7NTxLdHil0F2y4G+PvrlqN0qB43tRaKJPQEYhG+Rn
ppXeOk6/AbgOFXBQCPoVJWOjxwMX3ea3rSLM5C9xUP9Rsnf/fkngD6G6pOo2nYin
fgpINQDhGB/r8BJs69RNhvgdbN4aV7Bz8WGYqKF3DVhV+Di5zIOPNC9zoZQPey4d
uMS06OERG7Op8fFws3QoCzEywVdAbe/R+m5oeg875vLVvmONwDi52mqv4rgbfl+a
PhyyPzoR3hdIPEi13AQB5hmyLAcTDtvcib3beNLw586NXcYgQZdcbLmDkjVRDK4u
niE4QaRUGeRJD2+xAgMBAAGjbzBtMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYI
KwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBSQ42eTfRBubU1JKWU45fWcZmLd
aTAfBgNVHSMEGDAWgBRvxexARA9ceASOZhFOoe4eODj9cjANBgkqhkiG9w0BAQsF
AAOCAQEAPvOKn7eKh09kVgjmoAfkufGKiFCgzJW9q34Clw/OG/5W/EQ8+rKY+c+0
pgeetMVkmkmQS4Fc7e7MOk/KNujdPOfBK2u5Yin4bcphU0AgMhuF+VUOhVXPv+m4
XewWqo0dhdgYGmqq4pHPuotGLejUqSSYILX34Ln+Lu/plBfDVFePf/gEkWrTDDof
UBwYwQj+yYNPIy/EuI3b0/JFVCcpK/NQfXkfOBcJijiQY4spjBJ/G9oOAbXHfZtl
mVs+3guwg9eBsA20CbRjbSqwJfUfz9+x/IrEKSk70yy6rYMhVtwee4G4d0rLCeIV
Mjt4aTOX/y/glIOdbSfQj/SunXs1GA==
-----END CERTIFICATE-----)";

// An EC certificate that doesn't match the public key kPublicKeyEcBase64.
// Taken from chrome/test/data/policy/test_certs/root_ca_cert_ec.pem
constexpr char kFakeEcCertificatePubKeyMismatch[] =
    R"(-----BEGIN CERTIFICATE-----
MIIBmDCCAT+gAwIBAgIUTIXqARj3mXjmzZDYD4qdPRMtc6owCgYIKoZIzj0EAwIw
GjEYMBYGA1UEAwwPcm9vdF9jYV9jZXJ0X2VjMB4XDTI0MDkxMjEwNTM1N1oXDTM0
MDkxMDEwNTM1N1owGjEYMBYGA1UEAwwPcm9vdF9jYV9jZXJ0X2VjMFkwEwYHKoZI
zj0CAQYIKoZIzj0DAQcDQgAEfvHw/Fj+p50zVlAfSxjIXdrTGL+Pxe3HzvtDCS+r
QFqJfDeT0Nd0EOQyPNmwb/qnGin9iXmDHuq0nz+Y3KtFg6NjMGEwDwYDVR0TAQH/
BAUwAwEB/zAdBgNVHQ4EFgQU2WQdI/7A5otWqaRzXqM1DONaiQkwHwYDVR0jBBgw
FoAU2WQdI/7A5otWqaRzXqM1DONaiQkwDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49
BAMCA0cAMEQCIA/yfOj1zcBxEBcXPMY6u4mBWTW74NqpKKpBgoJhj1zSAiATySyU
MOXls/JPQ/RCRDC6R4fRi4BRNJ9mt5p8QMQHxA==
-----END CERTIFICATE-----)";

constexpr char kCertProfileId[] = "cert_profile_1";
constexpr char kCertProfileName[] = "Certificate Profile 1";
constexpr char kCertProfileVersion[] = "cert_profile_version_1";
constexpr base::TimeDelta kCertProfileRenewalPeriod = base::Seconds(0);
// Prefix + certificate profile name.
constexpr char kChallenge[] = "fake_va_challenge_1";
constexpr char kChallengeResponse[] = "fake_va_challenge_response_1";
constexpr char kSignatureBase64[] = "AQIDBAU=";
// The signature was recorded from the code under test. A real CA successfully
// issued a cert for it, so it should be correct. It is not related to the
// kPublicKeyEcBase64 key, but it shouldn't matter for these unit tests.
constexpr char kEccSignatureAsn1Base64[] =
    "MEQCIHRmp42nHk9m/rx4cITQE7lkYG9NVFXQQgQHHOzmbMZhAiB/c/"
    "D3K3fFFeprb+IKs4cYLzX5d3JsGDXAca/eCzyaTg==";
constexpr unsigned int kNonVaKeyModulusLengthBits = 2048;
constexpr char kEcNamedCurve[] = "P-256";

// The delays before the first, second and third attempts to fetch the next
// instruction (in reality they could be a bit smaller because of the 10% jitter
// that is always subtracted).
constexpr base::TimeDelta kFetchInstructionFirstDelay = base::Seconds(5);
constexpr base::TimeDelta kFetchInstructionSecondDelay = base::Seconds(5);
constexpr base::TimeDelta kFetchInstructionThirdDelay =
    kFetchInstructionFirstDelay * 4;
constexpr base::TimeDelta kFetchInstructionFourthDelay =
    kFetchInstructionFirstDelay * 16;  // 4 ^ 2
constexpr base::TimeDelta kFetchInstructionFifthDelay =
    kFetchInstructionFirstDelay * 64;  // 4 ^ 3
// Starting with the fourth attempt the next instruction should be fetched with
// the max delay.
constexpr base::TimeDelta kFetchInstructionMaxDelay = base::Hours(8);
constexpr base::TimeDelta kRequestRetryInitialDelay = base::Seconds(30);
constexpr base::TimeDelta kSmallDelay = base::Milliseconds(500);

const std::string& GetPublicKey(KeyType key_type) {
  static base::NoDestructor<base::flat_map<KeyType, std::string>> public_key;
  if ((*public_key)[key_type].empty()) {
    base::Base64Decode(
        key_type == KeyType::kRsa ? kPublicKeyRsaBase64 : kPublicKeyEcBase64,
        &(*public_key)[key_type]);
  }
  return (*public_key)[key_type];
}

const std::vector<uint8_t>& GetPublicKeyBin(KeyType key_type) {
  static base::NoDestructor<
      base::flat_map<KeyType, std::optional<std::vector<uint8_t>>>>
      public_key;
  if (!(*public_key)[key_type].has_value()) {
    (*public_key)[key_type] = base::Base64Decode(
        key_type == KeyType::kRsa ? kPublicKeyRsaBase64 : kPublicKeyEcBase64);
    CHECK((*public_key)[key_type].has_value());
  }
  return (*public_key)[key_type].value();
}

std::string GetDataToSignStr() {
  return std::string({10, 11, 12, 13, 14});
}

std::vector<uint8_t> GetDataToSignBin() {
  return std::vector<uint8_t>({10, 11, 12, 13, 14});
}

std::string GetSignatureStr() {
  return std::string({1, 2, 3, 4, 5});
}

std::vector<uint8_t> GetSignatureBin() {
  return std::vector<uint8_t>({1, 2, 3, 4, 5});
}

std::vector<uint8_t> GetEccSignatureRawBin() {
  // The raw values from the kEccSignatureAsn1Base64 signature (concatenated to
  // each other) without the ASN.1 structure. This should be a realistic example
  // of what is returned from the SIgnEcdsa method.
  return base::Base64Decode(
             "dGanjaceT2b+vHhwhNATuWRgb01UVdBCBAcc7OZsxmF/c/"
             "D3K3fFFeprb+IKs4cYLzX5d3JsGDXAca/eCzyaTg==")
      .value();
}

std::string GetEccSignatureAsn1Str() {
  std::vector<uint8_t> asn1_signature =
      base::Base64Decode(kEccSignatureAsn1Base64).value();
  return std::string(asn1_signature.begin(), asn1_signature.end());
}

std::vector<uint8_t> GetCertProfileIdBin() {
  // -1 because of '\0'.
  return std::vector<uint8_t>(
      kCertProfileId, UNSAFE_TODO(kCertProfileId + sizeof(kCertProfileId) - 1));
}

void VerifyDeleteKeyCalledOnce(CertScope cert_scope) {
  const std::vector<::attestation::DeleteKeysRequest> delete_keys_history =
      AttestationClient::Get()->GetTestInterface()->delete_keys_history();
  EXPECT_EQ(delete_keys_history.size(), 1u);
  EXPECT_EQ(delete_keys_history[0].username().empty(),
            cert_scope != CertScope::kUser);
  EXPECT_EQ(delete_keys_history[0].key_label_match(),
            GetKeyName(kCertProfileId));
  EXPECT_EQ(delete_keys_history[0].match_behavior(),
            ::attestation::DeleteKeysRequest::MATCH_BEHAVIOR_EXACT);
}

using GetNextInstructionResult =
    base::expected<em::CertProvGetNextInstructionResponse,
                   CertProvisioningClient::Error>;

using StartResult =
    base::expected<em::CertProvStartResponse, CertProvisioningClient::Error>;

using NoDataResult = base::expected<void, CertProvisioningClient::Error>;

GetNextInstructionResult NextInstructionAuthorize() {
  em::CertProvGetNextInstructionResponse next_instruction_response;
  next_instruction_response.mutable_authorize_instruction()->set_va_challenge(
      kChallenge);

  return next_instruction_response;
}

GetNextInstructionResult NextInstructionProofOfPossession(KeyType key_type) {
  em::CertProvGetNextInstructionResponse next_instruction_response;
  next_instruction_response.mutable_proof_of_possession_instruction()
      ->set_data_to_sign(GetDataToSignStr());
  if (key_type == KeyType::kRsa) {
    next_instruction_response.mutable_proof_of_possession_instruction()
        ->set_signature_algorithm(
            enterprise_management::CertProvSignatureAlgorithm::
                SIGNATURE_ALGORITHM_RSA_PKCS1_V1_5_NO_HASH);
  } else if (key_type == KeyType::kEc) {
    next_instruction_response.mutable_proof_of_possession_instruction()
        ->set_signature_algorithm(
            enterprise_management::CertProvSignatureAlgorithm::
                SIGNATURE_ALGORITHM_ECDSA_SHA256);
  } else {
    NOTREACHED();
  }

  return next_instruction_response;
}

GetNextInstructionResult NextInstructionImportCertificate(
    const std::string& pem) {
  em::CertProvGetNextInstructionResponse next_instruction_response;
  next_instruction_response.mutable_import_certificate_instruction()
      ->set_pem_encoded_certificate(pem);

  return next_instruction_response;
}

StartResult StartResultOk() {
  em::CertProvStartResponse start_response;

  return start_response;
}

NoDataResult NoDataResultOk() {
  return {};
}

CertProvisioningClient::Error DmStatusError(
    policy::DeviceManagementStatus dm_status) {
  em::CertProvBackendError backend_error;
  return {dm_status, backend_error};
}

CertProvisioningClient::Error BackendError(
    em::CertProvBackendError::Error error) {
  em::CertProvBackendError backend_error;
  backend_error.set_error(error);
  backend_error.set_debug_message("Test debug message");
  return {policy::DM_STATUS_SUCCESS, backend_error};
}

CertProvisioningClient::Error InstructionNotYetAvailable() {
  return BackendError(em::CertProvBackendError::INSTRUCTION_NOT_YET_AVAILABLE);
}

// Using macros to reduce boilerplate code, but keep real line numbers in
// error messages in case of expectation failure. They use some of protected
// fields of CertProvisioningWorkerDynamicTest class and may be considered as
// extra methods of it. *_OK macros immediately call callbacks with some
// successful results. *_NO_OP doesn't call callbacks.
#define EXPECT_PREPARE_KEY_OK(MOCK_TPM_CHALLENGE_KEY, PREPARE_KEY_FUNC, \
                              KEY_TYPE)                                 \
  {                                                                     \
    auto public_key_result =                                            \
        attestation::TpmChallengeKeyResult::MakePublicKey(              \
            GetPublicKey(KEY_TYPE));                                    \
    EXPECT_CALL((MOCK_TPM_CHALLENGE_KEY), PREPARE_KEY_FUNC)             \
        .Times(1)                                                       \
        .WillOnce(RunOnceCallback<5>(public_key_result));               \
  }

#define EXPECT_SIGN_CHALLENGE_OK(MOCK_TPM_CHALLENGE_KEY, SIGN_CHALLENGE_FUNC) \
  {                                                                           \
    auto sign_challenge_result =                                              \
        attestation::TpmChallengeKeyResult::MakeChallengeResponse(            \
            kChallengeResponse);                                              \
    EXPECT_CALL((MOCK_TPM_CHALLENGE_KEY), SIGN_CHALLENGE_FUNC)                \
        .Times(1)                                                             \
        .WillOnce(RunOnceCallback<1>(sign_challenge_result));                 \
  }

#define EXPECT_REGISTER_KEY_OK(MOCK_TPM_CHALLENGE_KEY, REGISTER_KEY_FUNC) \
  {                                                                       \
    auto register_key_result =                                            \
        attestation::TpmChallengeKeyResult::MakeSuccess();                \
    EXPECT_CALL((MOCK_TPM_CHALLENGE_KEY), REGISTER_KEY_FUNC)              \
        .Times(1)                                                         \
        .WillOnce(RunOnceCallback<0>(register_key_result));               \
  }

#define EXPECT_START(START_FUNC, RESPONSE_VALUE)       \
  {                                                    \
    EXPECT_CALL(cert_provisioning_client_, START_FUNC) \
        .Times(1)                                      \
        .WillOnce(RunOnceCallback<1>(RESPONSE_VALUE)); \
  }

#define EXPECT_START_NO_OP(START_FUNC) \
  { EXPECT_CALL(cert_provisioning_client_, START_FUNC).Times(1); }

#define EXPECT_GET_NEXT_INSTRUCTION(GET_NEXT_INSTRUCTION_FUNC, RESPONSE_VALUE) \
  {                                                                            \
    EXPECT_CALL(cert_provisioning_client_, GET_NEXT_INSTRUCTION_FUNC)          \
        .Times(1)                                                              \
        .WillOnce(RunOnceCallback<1>(RESPONSE_VALUE));                         \
  }

#define EXPECT_GET_NEXT_INSTRUCTION_NO_OP(GET_NEXT_INSTRUCTION_FUNC)  \
  {                                                                   \
    EXPECT_CALL(cert_provisioning_client_, GET_NEXT_INSTRUCTION_FUNC) \
        .Times(1);                                                    \
  }

#define EXPECT_AUTHORIZE(AUTHORIZE_FUNC, RESPONSE_VALUE)   \
  {                                                        \
    EXPECT_CALL(cert_provisioning_client_, AUTHORIZE_FUNC) \
        .Times(1)                                          \
        .WillOnce(RunOnceCallback<2>(RESPONSE_VALUE));     \
  }

#define EXPECT_UPLOAD_PROOF_OF_POSSESSION(UPLOAD_FUNC, RESPONSE_VALUE) \
  {                                                                    \
    EXPECT_CALL(cert_provisioning_client_, UPLOAD_FUNC)                \
        .Times(1)                                                      \
        .WillOnce(RunOnceCallback<2>(RESPONSE_VALUE));                 \
  }

#define EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(SET_FUNC)        \
  {                                                      \
    EXPECT_CALL(*platform_keys_service_, SET_FUNC)       \
        .Times(1)                                        \
        .WillOnce(RunOnceCallback<4>(Status::kSuccess)); \
  }

#define EXPECT_SET_ATTRIBUTE_FOR_KEY_FAIL(SET_FUNC)            \
  {                                                            \
    EXPECT_CALL(*platform_keys_service_, SET_FUNC)             \
        .Times(1)                                              \
        .WillOnce(RunOnceCallback<4>(Status::kErrorInternal)); \
  }

#define EXPECT_SIGN_RSAPKC1_RAW_OK(SIGN_FUNC)                               \
  {                                                                         \
    EXPECT_CALL(*platform_keys_service_, SIGN_FUNC)                         \
        .Times(1)                                                           \
        .WillOnce(RunOnceCallback<3>(GetSignatureBin(), Status::kSuccess)); \
  }

#define EXPECT_SIGN_ECDSA_OK(SIGN_FUNC)                                     \
  {                                                                         \
    EXPECT_CALL(*platform_keys_service_, SIGN_FUNC)                         \
        .Times(1)                                                           \
        .WillOnce(                                                          \
            RunOnceCallback<4>(GetEccSignatureRawBin(), Status::kSuccess)); \
  }

#define EXPECT_IMPORT_CERTIFICATE_OK(IMPORT_FUNC)        \
  {                                                      \
    EXPECT_CALL(*platform_keys_service_, IMPORT_FUNC)    \
        .Times(1)                                        \
        .WillOnce(RunOnceCallback<2>(Status::kSuccess)); \
  }

// A mock for observing the state change callback of the worker.
class StateChangeCallbackObserver {
 public:
  MOCK_METHOD(void, StateChangeCallback, ());
};

class CertProvisioningWorkerDynamicTest : public ::testing::Test {
 public:
  CertProvisioningWorkerDynamicTest() { Init(); }
  CertProvisioningWorkerDynamicTest(const CertProvisioningWorkerDynamicTest&) =
      delete;
  CertProvisioningWorkerDynamicTest& operator=(
      const CertProvisioningWorkerDynamicTest&) = delete;
  ~CertProvisioningWorkerDynamicTest() override = default;

  void SetUp() override {
    AttestationClient::InitializeFake();

    RegisterProfilePrefs(testing_pref_service_.registry());
    RegisterLocalStatePrefs(testing_pref_service_.registry());
  }

  void TearDown() override {
    EXPECT_FALSE(
        attestation::TpmChallengeKeySubtleFactory::WillReturnTestingInstance());
    AttestationClient::Shutdown();
  }

 protected:
  void Init() {
    platform_keys_service_ =
        static_cast<platform_keys::MockPlatformKeysService*>(
            platform_keys::PlatformKeysServiceFactory::GetInstance()
                ->SetTestingFactoryAndUse(
                    GetProfile(),
                    base::BindRepeating(
                        &platform_keys::BuildMockPlatformKeysService)));
    ASSERT_TRUE(platform_keys_service_);
    platform_keys::PlatformKeysServiceFactory::GetInstance()
        ->SetDeviceWideServiceForTesting(platform_keys_service_);

    key_permissions_manager_ =
        std::make_unique<platform_keys::MockKeyPermissionsManager>();

    platform_keys::UserPrivateTokenKeyPermissionsManagerServiceFactory::
        GetInstance()
            ->SetTestingFactory(
                GetProfile(),
                base::BindRepeating(
                    &platform_keys::
                        BuildFakeUserPrivateTokenKeyPermissionsManagerService,
                    key_permissions_manager_.get()));
    platform_keys::KeyPermissionsManagerImpl::
        SetSystemTokenKeyPermissionsManagerForTesting(
            key_permissions_manager_.get());

    // Only explicitly expected removals are allowed.
    EXPECT_CALL(*platform_keys_service_, RemoveCertificate).Times(0);
    EXPECT_CALL(*platform_keys_service_, RemoveKey).Times(0);
  }

  void FastForwardBy(base::TimeDelta delta) {
    task_environment_.FastForwardBy(delta);
  }

  // Jump the mock time by `delta`, then run all scheduled tasks that should
  // have started by then.
  //
  // This can be useful if scheduled tasks happen in sequence, each scheduling
  // the next task when executed, and a test is verifying the delays between
  // them.
  //
  // Example:
  // 1. Delayed Task TaskA has been posted to run in 10s.
  //    When executed, the task will post TaskB with a delay of 10s.
  // 2. FastForwardBy(base::Seconds(15))
  // Now TaskB is scheduled to run after 5 mock seconds.
  //
  // 1. Delayed Task TaskA has been posted to run in 10s.
  //    When executed, the task will post TaskB with a delay of 10s.
  // 2. AdvanceClockAndRunTasks(base::Seconds(15)
  // Now TaskB is scheduled to run after 10 mock seconds.
  void AdvanceClockAndRunTasks(base::TimeDelta delta) {
    task_environment_.AdvanceClock(delta);
    task_environment_.RunUntilIdle();
  }

  // Checks that after `backoff_max_delay` the `worker` tries to fetch the next
  // instruction and that it doesn't try to do it too early.
  void VerifyWorkerTriesFetchingNextInstruction(
      const CertProvisioningClient::ProvisioningProcess& provisioning_process,
      const CertProvisioningWorkerDynamic& worker,
      base::TimeDelta backoff_max_delay) {
    // The jitter comes from the backoff policy in the worker. It will reduce
    // the actual waiting time by up to 10% compared to the max delay.
    const double kEffectiveJitterFactor = 0.9;
    const base::TimeDelta backoff_min_delay =
        backoff_max_delay * kEffectiveJitterFactor;

    testing::InSequence seq;

    // Verify that nothing happens until the backoff time is reached.
    AdvanceClockAndRunTasks(backoff_min_delay - kSmallDelay);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    // Advance the time to the moment after <this method begins> +
    // backoff_max_delay + kSmallDelay.
    AdvanceClockAndRunTasks(backoff_max_delay - backoff_min_delay +
                            2 * kSmallDelay);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);

    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  // Replaces next result of TpmChallengeKeySubtleFactory and return pointer to
  // the mock. The mock will injected into next created worker and will live
  // until worker's destruction. Should be called before creation of every
  // worker.
  MockTpmChallengeKeySubtle* PrepareTpmChallengeKey() {
    auto mock_tpm_challenge_key_subtle_impl =
        std::make_unique<MockTpmChallengeKeySubtle>();

    MockTpmChallengeKeySubtle* tpm_challenge_key_impl =
        mock_tpm_challenge_key_subtle_impl.get();

    attestation::TpmChallengeKeySubtleFactory::SetForTesting(
        std::move(mock_tpm_challenge_key_subtle_impl));

    CHECK(tpm_challenge_key_impl);
    return tpm_challenge_key_impl;
  }

  base::RepeatingClosure GetStateChangeCallback() {
    return base::BindRepeating(
        &StateChangeCallbackObserver ::StateChangeCallback,
        base::Unretained(&state_change_callback_observer_));
  }

  CertProvisioningWorkerCallback GetResultCallback() {
    return callback_observer_.GetCallback();
  }

  Profile* GetProfile() { return profile_helper_for_testing_.GetProfile(); }

  std::unique_ptr<MockCertProvisioningInvalidator> MakeInvalidator() {
    return std::make_unique<MockCertProvisioningInvalidator>();
  }

  std::unique_ptr<MockCertProvisioningInvalidator> MakeInvalidator(
      MockCertProvisioningInvalidator** mock_invalidator) {
    auto result = std::make_unique<MockCertProvisioningInvalidator>();
    *mock_invalidator = result.get();
    return result;
  }

  content::BrowserTaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};

  StrictMock<StateChangeCallbackObserver> state_change_callback_observer_;
  base::test::TestFuture<CertProfile, std::string, CertProvisioningWorkerState>
      callback_observer_;
  ProfileHelperForTesting profile_helper_for_testing_;
  TestingPrefServiceSimple testing_pref_service_;

  StrictMock<MockCertProvisioningClient> cert_provisioning_client_;
  raw_ptr<platform_keys::MockPlatformKeysService> platform_keys_service_ =
      nullptr;
  std::unique_ptr<platform_keys::MockKeyPermissionsManager>
      key_permissions_manager_;
};

// Checks that the worker makes all necessary requests to other modules during
// success scenario.
// The worker gets a "no next operation available yet" response on each initial
// GetNextOperation query and then retries after some delay.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, SuccessWithAllStepsRsaKeys) {
  base::HistogramTester histogram_tester;

  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const std::string listener_type = MakeInvalidationListenerType(process_id);
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  MockCertProvisioningInvalidator* mock_invalidator = nullptr;
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_,
      MakeInvalidator(&mock_invalidator), GetStateChangeCallback(),
      GetResultCallback());

  OnInvalidationEventCallback on_invalidation_event_callback;

  auto VerifyNoBackendErrorsSeen = [&worker]() {
    EXPECT_EQ(worker.GetLastBackendServerError(), std::nullopt);
  };
  {
    testing::InSequence seq;

    // kInitState
    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    // kKeypairGenerated
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_CALL(*mock_invalidator, Register(listener_type, _))
        .WillOnce(SaveArg<1>(&on_invalidation_event_callback));

    // kReadyForNextOperation
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    // Waiting mode.
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    worker.DoStep();
    on_invalidation_event_callback.Run(
        InvalidationEvent::kSuccessfullySubscribed);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }
  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    // kAuthorizeInstructionReceived
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));
    // kVaChallengeFinished
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);
    // kKeyRegistered
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));
    // kKeypairMarked
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    // kReadyForNextOperation
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }
  {
    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    // kReadyForNextOperation
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }
  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));

    // kProofOfPossessionInstructionReceived
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_SIGN_RSAPKC1_RAW_OK(
        SignRSAPKCS1Raw(::testing::Optional(TokenId::kUser), GetDataToSignBin(),
                        GetPublicKeyBin(KeyType::kRsa), /*callback=*/_));
    // kSignCsrFinished
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetSignatureStr(),
                                /*callback=*/_),
        NoDataResultOk());

    // kReadyForNextOperation
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }
  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeRsaCertificate));

    // kImportCertificateInstructionReceived
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));
    // kSucceeded
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_CALL(*mock_invalidator, Unregister()).Times(1);

    AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }

  histogram_tester.ExpectUniqueSample(
      "ChromeOS.CertProvisioning.Result.Dynamic.User",
      CertProvisioningWorkerState::kSucceeded, 1);
  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Event.Dynamic.User",
      CertProvisioningEvent::kRegisteredToInvalidationTopic, 1);
  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Event.Dynamic.User",
      CertProvisioningEvent::kSuccessfullySubscribedToInvalidationTopic, 1);
  histogram_tester.ExpectTotalCount(
      "ChromeOS.CertProvisioning.Event.Dynamic.User", 9);
}

// Checks that the worker makes all necessary requests to other modules during
// success scenario.
// The worker gets a "no next operation available yet" response on each initial
// GetNextOperation query and then retries after some delay.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, SuccessWithAllStepsEcKeys) {
  base::HistogramTester histogram_tester;

  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const std::string listener_type = MakeInvalidationListenerType(process_id);
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  MockCertProvisioningInvalidator* mock_invalidator = nullptr;
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_,
      MakeInvalidator(&mock_invalidator), GetStateChangeCallback(),
      GetResultCallback());

  OnInvalidationEventCallback on_invalidation_event_callback;

  auto VerifyNoBackendErrorsSeen = [&worker]() {
    EXPECT_EQ(worker.GetLastBackendServerError(), std::nullopt);
  };
  {
    testing::InSequence seq;

    // kInitState
    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    // kKeypairGenerated
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_CALL(*mock_invalidator, Register(listener_type, _))
        .WillOnce(SaveArg<1>(&on_invalidation_event_callback));

    // kReadyForNextOperation
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    // Waiting mode.
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    worker.DoStep();
    on_invalidation_event_callback.Run(
        InvalidationEvent::kSuccessfullySubscribed);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }
  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    // kAuthorizeInstructionReceived
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));
    // kVaChallengeFinished
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);
    // kKeyRegistered
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));
    // kKeypairMarked
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    // kReadyForNextOperation
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }
  {
    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    // kReadyForNextOperation
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }
  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kEc));

    // kProofOfPossessionInstructionReceived
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_SIGN_ECDSA_OK(SignEcdsa(
        ::testing::Optional(TokenId::kUser), GetDataToSignBin(),
        GetPublicKeyBin(KeyType::kEc),
        chromeos::platform_keys::HASH_ALGORITHM_SHA256, /*callback=*/_));
    // kSignCsrFinished
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetEccSignatureAsn1Str(),
                                /*callback=*/_),
        NoDataResultOk());

    // kReadyForNextOperation
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeEcCertificate));

    // kImportCertificateInstructionReceived
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));
    // kSucceeded
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_CALL(*mock_invalidator, Unregister()).Times(1);

    AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }

  histogram_tester.ExpectUniqueSample(
      "ChromeOS.CertProvisioning.Result.Dynamic.User",
      CertProvisioningWorkerState::kSucceeded, 1);
  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Event.Dynamic.User",
      CertProvisioningEvent::kRegisteredToInvalidationTopic, 1);
  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Event.Dynamic.User",
      CertProvisioningEvent::kSuccessfullySubscribedToInvalidationTopic, 1);
  histogram_tester.ExpectTotalCount(
      "ChromeOS.CertProvisioning.Event.Dynamic.User", 9);
}

// Checks that the worker makes all necessary requests to other modules during
// success scenario.
// The worker gets next instructions immediately after each operation.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, SuccessWithAllStepsNoWaitingRsaKeys) {
  base::HistogramTester histogram_tester;

  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const std::string listener_type = MakeInvalidationListenerType(process_id);
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  MockCertProvisioningInvalidator* mock_invalidator = nullptr;
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_,
      MakeInvalidator(&mock_invalidator), GetStateChangeCallback(),
      GetResultCallback());

  auto VerifyNoBackendErrorsSeen = [&worker]() {
    EXPECT_EQ(worker.GetLastBackendServerError(), std::nullopt);
  };
  {
    testing::InSequence seq;

    // kInitState
    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    // kKeypairGenerated
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    OnInvalidationEventCallback on_invalidation_event_callback;
    EXPECT_CALL(*mock_invalidator, Register(listener_type, _))
        .WillOnce(SaveArg<1>(&on_invalidation_event_callback));

    // kReadyForNextOperation
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    // kAuthorizeInstructionReceived
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));
    // kVaChallengeFinished
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);
    // kKeyRegistered
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));
    // kKeypairMarked
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    // kReadyForNextOperation
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));

    // kProofOfPossessionInstructionReceived
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_SIGN_RSAPKC1_RAW_OK(
        SignRSAPKCS1Raw(::testing::Optional(TokenId::kUser), GetDataToSignBin(),
                        GetPublicKeyBin(KeyType::kRsa), /*callback=*/_));
    // kSignCsrFinished
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetSignatureStr(),
                                /*callback=*/_),
        NoDataResultOk());

    // kReadyForNextOperation
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeRsaCertificate));

    // kImportCertificateInstructionReceived
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));
    // kSucceeded
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_CALL(*mock_invalidator, Unregister()).Times(1);

    worker.DoStep();
    // kReadyForNextOperation after calling Authorize.
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    on_invalidation_event_callback.Run(
        InvalidationEvent::kInvalidationReceived);
    // kReadyForNextOperation after calling UploadProofOfPossession.
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    on_invalidation_event_callback.Run(
        InvalidationEvent::kInvalidationReceived);
    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }

  histogram_tester.ExpectUniqueSample(
      "ChromeOS.CertProvisioning.Result.Dynamic.User",
      CertProvisioningWorkerState::kSucceeded, 1);
  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Event.Dynamic.User",
      CertProvisioningEvent::kRegisteredToInvalidationTopic, 1);
  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Event.Dynamic.User",
      CertProvisioningEvent::kInvalidationReceived, 2);
  histogram_tester.ExpectUniqueSample(kDmStatusHistogramName,
                                      policy::DM_STATUS_SUCCESS, 6);
}

// Checks that the worker makes all necessary requests to other modules during
// success scenario.
// The worker gets next instructions immediately after each operation.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, SuccessWithAllStepsNoWaitingEcKeys) {
  base::HistogramTester histogram_tester;

  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const std::string listener_type = MakeInvalidationListenerType(process_id);
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  MockCertProvisioningInvalidator* mock_invalidator = nullptr;
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_,
      MakeInvalidator(&mock_invalidator), GetStateChangeCallback(),
      GetResultCallback());

  auto VerifyNoBackendErrorsSeen = [&worker]() {
    EXPECT_EQ(worker.GetLastBackendServerError(), std::nullopt);
  };
  {
    testing::InSequence seq;

    // kInitState
    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    // kKeypairGenerated
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    OnInvalidationEventCallback on_invalidation_event_callback;
    EXPECT_CALL(*mock_invalidator, Register(listener_type, _))
        .WillOnce(SaveArg<1>(&on_invalidation_event_callback));

    // kReadyForNextOperation
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    // kAuthorizeInstructionReceived
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));
    // kVaChallengeFinished
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);
    // kKeyRegistered
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));
    // kKeypairMarked
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    // kReadyForNextOperation
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kEc));

    // kProofOfPossessionInstructionReceived
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_SIGN_ECDSA_OK(SignEcdsa(
        ::testing::Optional(TokenId::kUser), GetDataToSignBin(),
        GetPublicKeyBin(KeyType::kEc),
        chromeos::platform_keys::HASH_ALGORITHM_SHA256, /*callback=*/_));
    // kSignCsrFinished
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetEccSignatureAsn1Str(),
                                /*callback=*/_),
        NoDataResultOk());

    // kReadyForNextOperation
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeEcCertificate));

    // kImportCertificateInstructionReceived
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));
    // kSucceeded
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce(VerifyNoBackendErrorsSeen);

    EXPECT_CALL(*mock_invalidator, Unregister()).Times(1);

    worker.DoStep();
    // kReadyForNextOperation after calling Authorize.
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    on_invalidation_event_callback.Run(
        InvalidationEvent::kInvalidationReceived);
    // kReadyForNextOperation after calling UploadProofOfPossession.
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    on_invalidation_event_callback.Run(
        InvalidationEvent::kInvalidationReceived);
    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }

  histogram_tester.ExpectUniqueSample(
      "ChromeOS.CertProvisioning.Result.Dynamic.User",
      CertProvisioningWorkerState::kSucceeded, 1);
  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Event.Dynamic.User",
      CertProvisioningEvent::kRegisteredToInvalidationTopic, 1);
  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Event.Dynamic.User",
      CertProvisioningEvent::kInvalidationReceived, 2);
}

// Checks that the worker doesn't allow skipping a VA challenge if VA was
// enabled.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, VaChallengeRequiredRsaKeys) {
  const CertScope kCertScope = CertScope::kUser;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));
  }

  worker.DoStep();
  FastForwardBy(base::Seconds(1));

  VerifyDeleteKeyCalledOnce(kCertScope);

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);
}

// Checks that the worker doesn't allow skipping a VA challenge if VA was
// enabled.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, VaChallengeRequiredEcKeys) {
  const CertScope kCertScope = CertScope::kUser;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kEc));
  }

  worker.DoStep();
  FastForwardBy(base::Seconds(1));

  VerifyDeleteKeyCalledOnce(kCertScope);

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);
}

// Checks that omitting the "proof of possession" step works fine.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, NoProofOfPossessionRsaKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  MockCertProvisioningInvalidator* mock_invalidator = nullptr;
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_,
      MakeInvalidator(&mock_invalidator), GetStateChangeCallback(),
      GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));
    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeRsaCertificate));

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));

    EXPECT_CALL(*mock_invalidator, Unregister()).Times(1);

    worker.DoStep();
    AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }
}

// Checks that omitting the "proof of possession" step works fine.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, NoProofOfPossessionEcKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  MockCertProvisioningInvalidator* mock_invalidator = nullptr;
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_,
      MakeInvalidator(&mock_invalidator), GetStateChangeCallback(),
      GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));
    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeEcCertificate));

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));

    EXPECT_CALL(*mock_invalidator, Unregister()).Times(1);

    worker.DoStep();
    AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }
}

// Checks that the worker makes all necessary requests to other modules during
// success scenario when VA challenge is not enabled.
// Provides one proof of possession .
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, NoVaSuccessRsaKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/false, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_CALL(*platform_keys_service_,
                GenerateRSAKey(TokenId::kUser, kNonVaKeyModulusLengthBits,
                               /*sw_backed=*/false,
                               /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<3>(GetPublicKeyBin(KeyType::kRsa),
                                     Status::kSuccess));

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));

    EXPECT_SIGN_RSAPKC1_RAW_OK(
        SignRSAPKCS1Raw(::testing::Optional(TokenId::kUser), GetDataToSignBin(),
                        GetPublicKeyBin(KeyType::kRsa), /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetSignatureStr(),
                                /*callback=*/_),
        NoDataResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeRsaCertificate));

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));
  }

  worker.DoStep();

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kSucceeded);
}

// Checks that the worker makes all necessary requests to other modules during
// success scenario when VA challenge is not enabled.
// Provides one proof of possession.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, NoVaSuccessEcKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/false, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_CALL(*platform_keys_service_,
                GenerateECKey(TokenId::kUser, kEcNamedCurve,
                              /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(GetPublicKeyBin(KeyType::kEc),
                                     Status::kSuccess));
    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));
    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));
    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());
    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kEc));
    EXPECT_SIGN_ECDSA_OK(SignEcdsa(
        ::testing::Optional(TokenId::kUser), GetDataToSignBin(),
        GetPublicKeyBin(KeyType::kEc),
        chromeos::platform_keys::HASH_ALGORITHM_SHA256, /*callback=*/_));
    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetEccSignatureAsn1Str(),
                                /*callback=*/_),
        NoDataResultOk());
    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeEcCertificate));
    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));
  }
  worker.DoStep();
  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kSucceeded);
}

// Checks that the worker fails if the server requests more than 1
// proof-of-possession signatures in the "verified access" case.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest,
       VaTooManyTwoProofsOfPossessionRsaKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));
    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_AUTHORIZE(Authorize(Eq(std::ref(provisioning_process)),
                               kChallengeResponse, /*callback=*/_),
                     NoDataResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));

    EXPECT_SIGN_RSAPKC1_RAW_OK(
        SignRSAPKCS1Raw(::testing::Optional(TokenId::kUser), GetDataToSignBin(),
                        GetPublicKeyBin(KeyType::kRsa), /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetSignatureStr(),
                                /*callback=*/_),
        NoDataResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));

    EXPECT_CALL(
        *platform_keys_service_,
        RemoveKey(TokenId::kUser,
                  /*public_key_spki_der=*/GetPublicKeyBin(KeyType::kRsa),
                  /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(Status::kSuccess));
  }

  worker.DoStep();

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);
}

// Checks that the worker fails if the server requests more than 1
// proof-of-possession signatures in the "verified access" case.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest,
       VaTooManyTwoProofsOfPossessionEcKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));
    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_AUTHORIZE(Authorize(Eq(std::ref(provisioning_process)),
                               kChallengeResponse, /*callback=*/_),
                     NoDataResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kEc));

    EXPECT_SIGN_ECDSA_OK(SignEcdsa(
        ::testing::Optional(TokenId::kUser), GetDataToSignBin(),
        GetPublicKeyBin(KeyType::kEc),
        chromeos::platform_keys::HASH_ALGORITHM_SHA256, /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetEccSignatureAsn1Str(),
                                /*callback=*/_),
        NoDataResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kEc));

    EXPECT_CALL(*platform_keys_service_,
                RemoveKey(TokenId::kUser,
                          /*public_key_spki_der=*/GetPublicKeyBin(KeyType::kEc),
                          /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(Status::kSuccess));
  }

  worker.DoStep();

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);
}

// Checks that the worker correctly handles a "proof of possession" instruction
// that doesn't explicitly specify any signature algorithm. This should result
// in the certificate provisioning process failing.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest,
       ProofOfPossessionNoSignatureAlgorithmRsaKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/false, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_CALL(*platform_keys_service_,
                GenerateRSAKey(TokenId::kUser, kNonVaKeyModulusLengthBits,
                               /*sw_backed=*/false,
                               /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<3>(GetPublicKeyBin(KeyType::kRsa),
                                     Status::kSuccess));

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    em::CertProvGetNextInstructionResponse proof_of_possession_instruction;
    proof_of_possession_instruction.mutable_proof_of_possession_instruction()
        ->set_data_to_sign(GetDataToSignStr());
    // Do not set `signature_algorithm` on `proof_of_possession_instruction`.

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        std::move(proof_of_possession_instruction));

    EXPECT_CALL(
        *platform_keys_service_,
        RemoveKey(TokenId::kUser,
                  /*public_key_spki_der=*/GetPublicKeyBin(KeyType::kRsa),
                  /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(Status::kSuccess));
  }

  worker.DoStep();
  // kReadyForNextOperation after starting the process.
  EXPECT_EQ(worker.GetState(),
            CertProvisioningWorkerState::kReadyForNextOperation);
  AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
  EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kFailed);

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);
}

// Checks that the worker correctly handles a "proof of possession" instruction
// that doesn't explicitly specify any signature algorithm. This should result
// in the certificate provisioning process failing.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest,
       ProofOfPossessionNoSignatureAlgorithmEcKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/false, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_CALL(*platform_keys_service_,
                GenerateECKey(TokenId::kUser, kEcNamedCurve,
                              /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(GetPublicKeyBin(KeyType::kEc),
                                     Status::kSuccess));

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    em::CertProvGetNextInstructionResponse proof_of_possession_instruction;
    proof_of_possession_instruction.mutable_proof_of_possession_instruction()
        ->set_data_to_sign(GetDataToSignStr());
    // Do not set `signature_algorithm` on `proof_of_possession_instruction`.

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        std::move(proof_of_possession_instruction));

    EXPECT_CALL(*platform_keys_service_,
                RemoveKey(TokenId::kUser,
                          /*public_key_spki_der=*/GetPublicKeyBin(KeyType::kEc),
                          /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(Status::kSuccess));
  }

  worker.DoStep();
  // kReadyForNextOperation after starting the process.
  EXPECT_EQ(worker.GetState(),
            CertProvisioningWorkerState::kReadyForNextOperation);
  AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
  EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kFailed);

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);
}

// Checks that the worker fails if the server requests more than one
// proof-of-possession signatures in the "no verified access" case.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest,
       NoVaTooManyTwoProofsOfPossessionRsaKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/false, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_CALL(*platform_keys_service_,
                GenerateRSAKey(TokenId::kUser, kNonVaKeyModulusLengthBits,
                               /*sw_backed=*/false,
                               /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<3>(GetPublicKeyBin(KeyType::kRsa),
                                     Status::kSuccess));

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));

    EXPECT_SIGN_RSAPKC1_RAW_OK(
        SignRSAPKCS1Raw(::testing::Optional(TokenId::kUser), GetDataToSignBin(),
                        GetPublicKeyBin(KeyType::kRsa), /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetSignatureStr(),
                                /*callback=*/_),
        NoDataResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));

    EXPECT_CALL(
        *platform_keys_service_,
        RemoveKey(TokenId::kUser,
                  /*public_key_spki_der=*/GetPublicKeyBin(KeyType::kRsa),
                  /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(Status::kSuccess));
  }

  worker.DoStep();

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);
}

// Checks that the worker fails if the server requests more than one
// proof-of-possession signatures in the "no verified access" case.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest,
       NoVaTooManyTwoProofsOfPossessionEcKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/false, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_CALL(*platform_keys_service_,
                GenerateECKey(TokenId::kUser, kEcNamedCurve,
                              /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(GetPublicKeyBin(KeyType::kEc),
                                     Status::kSuccess));

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kEc));

    EXPECT_SIGN_ECDSA_OK(SignEcdsa(
        ::testing::Optional(TokenId::kUser), GetDataToSignBin(),
        GetPublicKeyBin(KeyType::kEc),
        chromeos::platform_keys::HASH_ALGORITHM_SHA256, /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetEccSignatureAsn1Str(),
                                /*callback=*/_),
        NoDataResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kEc));

    EXPECT_CALL(*platform_keys_service_,
                RemoveKey(TokenId::kUser,
                          /*public_key_spki_der=*/GetPublicKeyBin(KeyType::kEc),
                          /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(Status::kSuccess));
  }

  worker.DoStep();

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);
}

// Checks that the worker makes all necessary requests to other modules during
// success scenario when VA challenge is not enabled.
// Provides one proof of possession.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, PublicKeyMismatchRsaKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/false, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_CALL(*platform_keys_service_,
                GenerateRSAKey(TokenId::kUser, kNonVaKeyModulusLengthBits,
                               /*sw_backed=*/false,
                               /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<3>(GetPublicKeyBin(KeyType::kRsa),
                                     Status::kSuccess));

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));

    EXPECT_SIGN_RSAPKC1_RAW_OK(
        SignRSAPKCS1Raw(::testing::Optional(TokenId::kUser), GetDataToSignBin(),
                        GetPublicKeyBin(KeyType::kRsa), /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetSignatureStr(),
                                /*callback=*/_),
        NoDataResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeRsaCertificatePubKeyMismatch));

    EXPECT_CALL(
        *platform_keys_service_,
        RemoveKey(TokenId::kUser,
                  /*public_key_spki_der=*/GetPublicKeyBin(KeyType::kRsa),
                  /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(Status::kSuccess));
  }

  worker.DoStep();

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);
}

// Checks that the worker makes all necessary requests to other modules during
// success scenario when VA challenge is not enabled.
// Provides one proof of possession.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, PublicKeyMismatchEcKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/false, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_CALL(*platform_keys_service_,
                GenerateECKey(TokenId::kUser, kEcNamedCurve,
                              /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(GetPublicKeyBin(KeyType::kEc),
                                     Status::kSuccess));

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kEc));

    EXPECT_SIGN_ECDSA_OK(SignEcdsa(
        ::testing::Optional(TokenId::kUser), GetDataToSignBin(),
        GetPublicKeyBin(KeyType::kEc),
        chromeos::platform_keys::HASH_ALGORITHM_SHA256, /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetEccSignatureAsn1Str(),
                                /*callback=*/_),
        NoDataResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeEcCertificatePubKeyMismatch));

    EXPECT_CALL(*platform_keys_service_,
                RemoveKey(TokenId::kUser,
                          /*public_key_spki_der=*/GetPublicKeyBin(KeyType::kEc),
                          /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(Status::kSuccess));
  }

  worker.DoStep();

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);
}

// Checks that when the server returns INSTRUCTION_NOT_YET_AVAILABLE, the
// worker will retry a request when it asked to continue the provisioning.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, TryLaterManualRetryRsaKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kDevice, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kDevice, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(
        *mock_tpm_challenge_key,
        StartPrepareKeyStep(::attestation::ENTERPRISE_MACHINE,
                            /*will_register_key=*/true,
                            ::attestation::KEY_TYPE_RSA,
                            /*key_name=*/GetKeyName(kCertProfileId),
                            /*profile=*/_,
                            /*callback=*/_, /*signals=*/_),
        KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));

    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kSystem, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }
  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));

    EXPECT_SIGN_RSAPKC1_RAW_OK(SignRSAPKCS1Raw(
        ::testing::Optional(TokenId::kSystem), GetDataToSignBin(),
        GetPublicKeyBin(KeyType::kRsa), /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetSignatureStr(),
                                /*callback=*/_),
        NoDataResultOk());

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeRsaCertificate));

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kSystem, /*certificate=*/_, /*callback=*/_));

    worker.DoStep();
    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }
}

// Checks that when the server returns INSTRUCTION_NOT_YET_AVAILABLE, the
// worker will retry a request when it asked to continue the provisioning.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, TryLaterManualRetryEcKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kDevice, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kDevice, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(
        *mock_tpm_challenge_key,
        StartPrepareKeyStep(::attestation::ENTERPRISE_MACHINE,
                            /*will_register_key=*/true,
                            ::attestation::KEY_TYPE_ECC,
                            /*key_name=*/GetKeyName(kCertProfileId),
                            /*profile=*/_,
                            /*callback=*/_, /*signals=*/_),
        KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));

    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kSystem, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }
  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kEc));

    EXPECT_SIGN_ECDSA_OK(SignEcdsa(
        ::testing::Optional(TokenId::kSystem), GetDataToSignBin(),
        GetPublicKeyBin(KeyType::kEc),
        chromeos::platform_keys::HASH_ALGORITHM_SHA256, /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetEccSignatureAsn1Str(),
                                /*callback=*/_),
        NoDataResultOk());

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeEcCertificate));

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kSystem, /*certificate=*/_, /*callback=*/_));

    worker.DoStep();
    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }
}

// Checks that when the server returns INSTRUCTION_NOT_YET_AVAILABLE, the worker
// will automatically retry a request after some time.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, TryLaterWaitRsaKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    // This GetNextInstruction will be called immedaitelly after Start because
    // VA is enabled. The following ones will wait.
    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));

    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    // Because the first attempt to get the next instruction after Start
    // returned "not available yet", the second attempt will be done after
    // kFetchInstructionSecondDelay.
    AdvanceClockAndRunTasks(kFetchInstructionSecondDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    // After the successful Authorize call the fetch next instruction delay
    // resets and will be done after kFetchInstructionFirstDelay.
    AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));

    EXPECT_SIGN_RSAPKC1_RAW_OK(
        SignRSAPKCS1Raw(::testing::Optional(TokenId::kUser), GetDataToSignBin(),
                        GetPublicKeyBin(KeyType::kRsa), /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetSignatureStr(),
                                /*callback=*/_),
        NoDataResultOk());

    // Because the first attempt to get the next instruction after Authorize
    // returned "not available yet", the second attempt will be done after
    // kFetchInstructionSecondDelay.
    AdvanceClockAndRunTasks(kFetchInstructionSecondDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    // After the successful UploadProofOfPossession call the fetch next
    // instruction delay resets and will be done after
    // kFetchInstructionFirstDelay.
    AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeRsaCertificate));

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));

    // Because the first attempt to get the next instruction after
    // UploadProofOfPossession returned "not available yet", the second attempt
    // will be done after kFetchInstructionSecondDelay.
    AdvanceClockAndRunTasks(kFetchInstructionSecondDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }
}

// Checks that when the server returns INSTRUCTION_NOT_YET_AVAILABLE, the worker
// will keep retrying the request with a progressively increasing delay.
TEST_F(CertProvisioningWorkerDynamicTest, FetchNextInstructionWithBackOff) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/false, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_CALL(*platform_keys_service_,
                GenerateRSAKey(TokenId::kUser, kNonVaKeyModulusLengthBits,
                               /*sw_backed=*/false,
                               /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<3>(GetPublicKeyBin(KeyType::kRsa),
                                     Status::kSuccess));

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  // Simulate several attempts to fetch the next instruction and check that the
  // worker waits the correct amount of time between them.
  VerifyWorkerTriesFetchingNextInstruction(provisioning_process, worker,
                                           kFetchInstructionFirstDelay);
  VerifyWorkerTriesFetchingNextInstruction(provisioning_process, worker,
                                           kFetchInstructionSecondDelay);
  VerifyWorkerTriesFetchingNextInstruction(provisioning_process, worker,
                                           kFetchInstructionThirdDelay);
  VerifyWorkerTriesFetchingNextInstruction(provisioning_process, worker,
                                           kFetchInstructionFourthDelay);
  VerifyWorkerTriesFetchingNextInstruction(provisioning_process, worker,
                                           kFetchInstructionFifthDelay);
  VerifyWorkerTriesFetchingNextInstruction(provisioning_process, worker,
                                           kFetchInstructionMaxDelay);

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));

    EXPECT_SIGN_RSAPKC1_RAW_OK(
        SignRSAPKCS1Raw(::testing::Optional(TokenId::kUser), GetDataToSignBin(),
                        GetPublicKeyBin(KeyType::kRsa), /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetSignatureStr(),
                                /*callback=*/_),
        NoDataResultOk());

    AdvanceClockAndRunTasks(kFetchInstructionMaxDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  // Verify that after successfully getting the next instruction the wait times
  // are reset.
  VerifyWorkerTriesFetchingNextInstruction(provisioning_process, worker,
                                           kFetchInstructionFirstDelay);

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeRsaCertificate));

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));

    FastForwardBy(kFetchInstructionSecondDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }
}

// Checks that when the server returns INSTRUCTION_NOT_YET_AVAILABLE, the worker
// will automatically retry a request after some time.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, TryLaterWaitEcKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    // This GetNextInstruction will be called immedaitelly after Start because
    // VA is enabled. The following ones will wait.
    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));

    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    // Because the first attempt to get the next instruction after Start
    // returned "not available yet", the second attempt will be done after
    // kFetchInstructionSecondDelay.
    AdvanceClockAndRunTasks(kFetchInstructionSecondDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    // After the successful Authorize call the fetch next instruction delay
    // resets and will be done after kFetchInstructionFirstDelay.
    AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kEc));

    EXPECT_SIGN_ECDSA_OK(SignEcdsa(
        ::testing::Optional(TokenId::kUser), GetDataToSignBin(),
        GetPublicKeyBin(KeyType::kEc),
        chromeos::platform_keys::HASH_ALGORITHM_SHA256, /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetEccSignatureAsn1Str(),
                                /*callback=*/_),
        NoDataResultOk());

    // Because the first attempt to get the next instruction after Authorize
    // returned "not available yet", the second attempt will be done after
    // kFetchInstructionSecondDelay.
    AdvanceClockAndRunTasks(kFetchInstructionSecondDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    // After the successful UploadProofOfPossession call the fetch next
    // instruction delay resets and will be done after
    // kFetchInstructionFirstDelay.
    AdvanceClockAndRunTasks(kFetchInstructionFirstDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeEcCertificate));

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));

    // Because the first attempt to get the next instruction after
    // UploadProofOfPossession returned "not available yet", the second attempt
    // will be done after kFetchInstructionSecondDelay.
    AdvanceClockAndRunTasks(kFetchInstructionSecondDelay + kSmallDelay);
    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }
}

// Test that with kCertProvisioningUseOnlyInvalidationsForTesting feature flag
// enabled the worker only progresses when it receives an invalidation.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, TryLaterWaitForInvalidationRsaKeys) {
  base::test::ScopedFeatureList scoped_feature_list{
      kCertProvisioningUseOnlyInvalidationsForTesting};

  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  const base::TimeDelta very_long_delay = base::Days(7);

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  {
    // Verify that the worker doesn't progress without an invalidation.
    FastForwardBy(very_long_delay);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));

    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    // Emulate an invalidation.
    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  {
    // Verify that the worker doesn't progress without an invalidation.
    FastForwardBy(very_long_delay);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));

    EXPECT_SIGN_RSAPKC1_RAW_OK(
        SignRSAPKCS1Raw(::testing::Optional(TokenId::kUser), GetDataToSignBin(),
                        GetPublicKeyBin(KeyType::kRsa), /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetSignatureStr(),
                                /*callback=*/_),
        NoDataResultOk());

    // Emulate an invalidation.
    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  {
    // Verify that the worker doesn't progress without an invalidation.
    FastForwardBy(very_long_delay);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeRsaCertificate));

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));

    // Emulate an invalidation.
    worker.DoStep();
    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);
  }
}

// Test that with kCertProvisioningUseOnlyInvalidationsForTesting feature flag
// enabled the worker only progresses when it receives an invalidation.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, TryLaterWaitForInvalidationEcKeys) {
  base::test::ScopedFeatureList scoped_feature_list{
      kCertProvisioningUseOnlyInvalidationsForTesting};

  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  const base::TimeDelta very_long_delay = base::Days(7);

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  {
    // Verify that the worker doesn't progress without an invalidation.
    FastForwardBy(very_long_delay);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));

    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    // Emulate an invalidation.
    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  {
    // Verify that the worker doesn't progress without an invalidation.
    FastForwardBy(very_long_delay);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kEc));

    EXPECT_SIGN_ECDSA_OK(SignEcdsa(
        ::testing::Optional(TokenId::kUser), GetDataToSignBin(),
        GetPublicKeyBin(KeyType::kEc),
        chromeos::platform_keys::HASH_ALGORITHM_SHA256, /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetEccSignatureAsn1Str(),
                                /*callback=*/_),
        NoDataResultOk());

    // Emulate an invalidation.
    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
  }

  {
    // Verify that the worker doesn't progress without an invalidation.
    FastForwardBy(very_long_delay);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeEcCertificate));

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));

    // Emulate an invalidation.
    worker.DoStep();
    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);
  }
}

// Checks that when the server returns an error in the "DM Status", the worker
// will enter an error state and stop the provisioning.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, StatusErrorHandlingRsaKeys) {
  const CertScope kCertScope = CertScope::kUser;
  base::HistogramTester histogram_tester;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    EXPECT_START(
        Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(DmStatusError(policy::DM_STATUS_REQUEST_INVALID)));
  }

  worker.DoStep();
  FastForwardBy(base::Seconds(1));

  VerifyDeleteKeyCalledOnce(kCertScope);

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);

  histogram_tester.ExpectUniqueSample(kDmStatusHistogramName,
                                      policy::DM_STATUS_REQUEST_INVALID, 1);
}

// Checks that when the server returns an error in the "DM Status", the worker
// will enter an error state and stop the provisioning.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, StatusErrorHandlingEcKeys) {
  const CertScope kCertScope = CertScope::kUser;
  base::HistogramTester histogram_tester;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(
                     DmStatusError(policy::DM_STATUS_RESPONSE_DECODING_ERROR)));
  }

  worker.DoStep();
  FastForwardBy(base::Seconds(1));

  VerifyDeleteKeyCalledOnce(kCertScope);

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);

  histogram_tester.ExpectUniqueSample(
      kDmStatusHistogramName, policy::DM_STATUS_RESPONSE_DECODING_ERROR, 1);
}

// Checks that when the server returns an error encoded in the proto response ,
// the worker will enter an error state and stop the provisioning. Also check
// factory.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, ResponseErrorHandlingRsaKeys) {
  const CertScope kCertScope = CertScope::kUser;
  base::HistogramTester histogram_tester;

  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, kCertScope, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  auto worker = CertProvisioningWorkerFactory::Get()->Create(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    EXPECT_START(
        Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(BackendError(em::CertProvBackendError::CA_FAILURE)));
  }

  worker->DoStep();
  FastForwardBy(base::Seconds(1));

  VerifyDeleteKeyCalledOnce(kCertScope);

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);

  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Result.Dynamic.User",
      CertProvisioningWorkerState::kFailed, 1);
  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Result.Dynamic.User",
      CertProvisioningWorkerState::kKeypairGenerated, 1);
  histogram_tester.ExpectTotalCount(
      "ChromeOS.CertProvisioning.Result.Dynamic.User", 2);
  histogram_tester.ExpectUniqueSample(kCertProvBackendErrorHistogramName,
                                      em::CertProvBackendError::CA_FAILURE, 1);
}

// Checks that when the server returns an error encoded in the proto response ,
// the worker will enter an error state and stop the provisioning. Also check
// factory.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, ResponseErrorHandlingEcKeys) {
  const CertScope kCertScope = CertScope::kUser;
  base::HistogramTester histogram_tester;

  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, kCertScope, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  auto worker = CertProvisioningWorkerFactory::Get()->Create(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(BackendError(
                     em::CertProvBackendError::PUBSUB_TOPIC_NOT_FOUND)));
  }

  worker->DoStep();
  FastForwardBy(base::Seconds(1));

  VerifyDeleteKeyCalledOnce(kCertScope);

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);

  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Result.Dynamic.User",
      CertProvisioningWorkerState::kFailed, 1);
  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Result.Dynamic.User",
      CertProvisioningWorkerState::kKeypairGenerated, 1);
  histogram_tester.ExpectTotalCount(
      "ChromeOS.CertProvisioning.Result.Dynamic.User", 2);
  histogram_tester.ExpectUniqueSample(
      kCertProvBackendErrorHistogramName,
      em::CertProvBackendError::PUBSUB_TOPIC_NOT_FOUND, 1);
}

TEST_F(CertProvisioningWorkerDynamicTest,
       InconsistentDataErrorHandlingRsaKeys) {
  const CertScope kCertScope = CertScope::kUser;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, kCertScope, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  auto worker = CertProvisioningWorkerFactory::Get()->Create(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(BackendError(
                     em::CertProvBackendError::INCONSISTENT_DATA)));
  }

  worker->DoStep();
  FastForwardBy(base::Seconds(1));

  VerifyDeleteKeyCalledOnce(kCertScope);

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kInconsistentDataError);
}

TEST_F(CertProvisioningWorkerDynamicTest, InconsistentDataErrorHandlingEcKeys) {
  const CertScope kCertScope = CertScope::kUser;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, kCertScope, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  auto worker = CertProvisioningWorkerFactory::Get()->Create(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(BackendError(
                     em::CertProvBackendError::INCONSISTENT_DATA)));
  }

  worker->DoStep();
  FastForwardBy(base::Seconds(1));

  VerifyDeleteKeyCalledOnce(kCertScope);

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kInconsistentDataError);
}

TEST_F(CertProvisioningWorkerDynamicTest, ProfileNotFoundErrorHandlingRsaKeys) {
  const CertScope kCertScope = CertScope::kUser;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, kCertScope, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  auto worker = CertProvisioningWorkerFactory::Get()->Create(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(BackendError(
                     em::CertProvBackendError::PROFILE_NOT_FOUND)));
  }

  worker->DoStep();
  FastForwardBy(base::Seconds(1));

  VerifyDeleteKeyCalledOnce(kCertScope);

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  // PROFILE_NOT_FOUND is also mapped to kInconsistentDataError on the client.
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kInconsistentDataError);
}

TEST_F(CertProvisioningWorkerDynamicTest, ProfileNotFoundErrorHandlingEcKeys) {
  const CertScope kCertScope = CertScope::kUser;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, kCertScope, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  auto worker = CertProvisioningWorkerFactory::Get()->Create(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(BackendError(
                     em::CertProvBackendError::PROFILE_NOT_FOUND)));
  }

  worker->DoStep();
  FastForwardBy(base::Seconds(1));

  VerifyDeleteKeyCalledOnce(kCertScope);

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  // PROFILE_NOT_FOUND is also mapped to kInconsistentDataError on the client.
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kInconsistentDataError);
}

// Checks that when the server returns TEMPORARY_UNAVAILABLE status code, the
// worker will automatically retry a request using exponential backoff strategy.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, BackoffStrategyRsaKeys) {
  base::HistogramTester histogram_tester;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  base::TimeDelta next_delay = base::Seconds(30);

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(
                     DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
    worker.DoStep();
  }

  Mock::VerifyAndClearExpectations(&cert_provisioning_client_);

  {
    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(
                     DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
    FastForwardBy(next_delay + kSmallDelay * 10);
    next_delay *= 2;
  }

  Mock::VerifyAndClearExpectations(&cert_provisioning_client_);

  {
    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(
                     DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
    FastForwardBy(next_delay + kSmallDelay * 10);
    next_delay *= 2;
  }

  Mock::VerifyAndClearExpectations(&cert_provisioning_client_);

  {
    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(
                     DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
    FastForwardBy(next_delay + kSmallDelay);
    next_delay *= 2;
  }

  histogram_tester.ExpectUniqueSample(
      kDmStatusHistogramName, policy::DM_STATUS_TEMPORARY_UNAVAILABLE, 4);
}

// Checks that when the server returns TEMPORARY_UNAVAILABLE status code, the
// worker will automatically retry a request using exponential backoff strategy.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, BackoffStrategyEcKeys) {
  base::HistogramTester histogram_tester;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  base::TimeDelta next_delay = base::Seconds(30);

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(
                     DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
    worker.DoStep();
  }

  Mock::VerifyAndClearExpectations(&cert_provisioning_client_);

  {
    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(
                     DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
    FastForwardBy(next_delay + kSmallDelay * 10);
    next_delay *= 2;
  }

  Mock::VerifyAndClearExpectations(&cert_provisioning_client_);

  {
    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(
                     DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
    FastForwardBy(next_delay + kSmallDelay * 10);
    next_delay *= 2;
  }

  Mock::VerifyAndClearExpectations(&cert_provisioning_client_);

  {
    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(
                     DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
    FastForwardBy(next_delay + kSmallDelay);
    next_delay *= 2;
  }

  histogram_tester.ExpectUniqueSample(
      kDmStatusHistogramName, policy::DM_STATUS_TEMPORARY_UNAVAILABLE, 4);
}

// Checks that the worker retries Authorize if it failed with a server-side
// temporary error.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, RetryAuthorizeRsaKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));
    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        base::unexpected(
            DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));

    worker.DoStep();
  }

  {
    testing::InSequence seq;

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeRsaCertificate));

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));

    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kKeypairMarked);
    FastForwardBy(kRequestRetryInitialDelay + base::Milliseconds(100));
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    FastForwardBy(kFetchInstructionFirstDelay + base::Milliseconds(100));
    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }
}

// Checks that the worker retries Authorize if it failed with a server-side
// temporary error.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, RetryAuthorizeEcKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));
    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        base::unexpected(
            DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));

    worker.DoStep();
  }

  {
    testing::InSequence seq;

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeEcCertificate));

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));

    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kKeypairMarked);
    FastForwardBy(kRequestRetryInitialDelay + base::Milliseconds(100));
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    FastForwardBy(kFetchInstructionFirstDelay + base::Milliseconds(100));
    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }
}

// Checks that the worker retries UploadProofOfPossession if it failed with a
// server-side temporary error.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, RetryUploadProofOfPossessionRsaKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/false, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const std::string listener_type = MakeInvalidationListenerType(process_id);
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockCertProvisioningInvalidator* mock_invalidator = nullptr;
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_,
      MakeInvalidator(&mock_invalidator), GetStateChangeCallback(),
      GetResultCallback());

  OnInvalidationEventCallback on_invalidation_event_callback;

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_CALL(*platform_keys_service_,
                GenerateRSAKey(TokenId::kUser, kNonVaKeyModulusLengthBits,
                               /*sw_backed=*/false,
                               /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<3>(GetPublicKeyBin(KeyType::kRsa),
                                     Status::kSuccess));

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_CALL(*mock_invalidator, Register(listener_type, _))
        .WillOnce(SaveArg<1>(&on_invalidation_event_callback));

    worker.DoStep();
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));

    EXPECT_SIGN_RSAPKC1_RAW_OK(
        SignRSAPKCS1Raw(::testing::Optional(TokenId::kUser), GetDataToSignBin(),
                        GetPublicKeyBin(KeyType::kRsa), /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetSignatureStr(),
                                /*callback=*/_),
        base::unexpected(
            DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));

    on_invalidation_event_callback.Run(
        InvalidationEvent::kInvalidationReceived);
  }

  {
    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetSignatureStr(),
                                /*callback=*/_),
        NoDataResultOk());
    FastForwardBy(kRequestRetryInitialDelay + kSmallDelay);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeRsaCertificate));

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));

    on_invalidation_event_callback.Run(
        InvalidationEvent::kInvalidationReceived);

    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }
}

// Checks that the worker retries UploadProofOfPossession if it failed with a
// server-side temporary error.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, RetryUploadProofOfPossessionEcKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/false, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const std::string listener_type = MakeInvalidationListenerType(process_id);
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockCertProvisioningInvalidator* mock_invalidator = nullptr;
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_,
      MakeInvalidator(&mock_invalidator), GetStateChangeCallback(),
      GetResultCallback());

  OnInvalidationEventCallback on_invalidation_event_callback;

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_CALL(*platform_keys_service_,
                GenerateECKey(TokenId::kUser, kEcNamedCurve,
                              /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(GetPublicKeyBin(KeyType::kEc),
                                     Status::kSuccess));

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_CALL(*mock_invalidator, Register(listener_type, _))
        .WillOnce(SaveArg<1>(&on_invalidation_event_callback));

    worker.DoStep();
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kEc));

    EXPECT_SIGN_ECDSA_OK(SignEcdsa(
        ::testing::Optional(TokenId::kUser), GetDataToSignBin(),
        GetPublicKeyBin(KeyType::kEc),
        chromeos::platform_keys::HASH_ALGORITHM_SHA256, /*callback=*/_));

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetEccSignatureAsn1Str(),
                                /*callback=*/_),
        base::unexpected(
            DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
    on_invalidation_event_callback.Run(
        InvalidationEvent::kInvalidationReceived);
  }

  {
    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetEccSignatureAsn1Str(),
                                /*callback=*/_),
        NoDataResultOk());
    FastForwardBy(kRequestRetryInitialDelay + kSmallDelay);
    Mock::VerifyAndClearExpectations(&cert_provisioning_client_);
  }

  {
    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeEcCertificate));

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));

    on_invalidation_event_callback.Run(
        InvalidationEvent::kInvalidationReceived);

    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceeded);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }
}

// Checks that when the server returns TEMPORARY_UNAVAILABLE status code, the
// worker will update its BackendServerError attribute.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest,
       ProcessBackendServerErrorResponseRsaKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback());

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(
                     DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce([&worker]() {
          EXPECT_THAT(worker.GetLastBackendServerError(),
                      testing::Ne(std::nullopt));
        });
    worker.DoStep();
  }

  Mock::VerifyAndClearExpectations(&cert_provisioning_client_);

  {
    testing::InSequence seq;
    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce([&worker]() {
          EXPECT_THAT(worker.GetLastBackendServerError(),
                      testing::Eq(std::nullopt));
        });

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce([&worker]() {
          EXPECT_THAT(worker.GetLastBackendServerError(),
                      testing::Eq(std::nullopt));
        });

    worker.DoStep();
  }
}

// Checks that when the server returns TEMPORARY_UNAVAILABLE status code, the
// worker will update its BackendServerError attribute.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest,
       ProcessBackendServerErrorResponseEcKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback());

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 base::unexpected(
                     DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce([&worker]() {
          EXPECT_THAT(worker.GetLastBackendServerError(),
                      testing::Ne(std::nullopt));
        });
    worker.DoStep();
  }

  Mock::VerifyAndClearExpectations(&cert_provisioning_client_);

  {
    testing::InSequence seq;
    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce([&worker]() {
          EXPECT_THAT(worker.GetLastBackendServerError(),
                      testing::Eq(std::nullopt));
        });

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));
    EXPECT_CALL(state_change_callback_observer_, StateChangeCallback())
        .WillOnce([&worker]() {
          EXPECT_THAT(worker.GetLastBackendServerError(),
                      testing::Eq(std::nullopt));
        });

    worker.DoStep();
  }
}

// Checks that the worker removes a key when an error occurs after the key was
// registered.
// This test covers the case when RSA keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, RemoveRegisteredKeyRsaKeys) {
  base::HistogramTester histogram_tester;

  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const std::string listener_type = MakeInvalidationListenerType(process_id);
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  MockCertProvisioningInvalidator* mock_invalidator = nullptr;
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_,
      MakeInvalidator(&mock_invalidator), GetStateChangeCallback(),
      GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_CALL(*mock_invalidator, Register(listener_type, _)).Times(1);

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));

    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_FAIL(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_CALL(*mock_invalidator, Unregister()).Times(1);

    EXPECT_CALL(
        *platform_keys_service_,
        RemoveKey(TokenId::kUser,
                  /*public_key_spki_der=*/GetPublicKeyBin(KeyType::kRsa),
                  /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(Status::kSuccess));
  }

  worker.DoStep();
  FastForwardBy(base::Seconds(1));

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);

  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Result.Dynamic.User",
      CertProvisioningWorkerState::kFailed, 1);
  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Result.Dynamic.User",
      CertProvisioningWorkerState::kKeyRegistered, 1);
  histogram_tester.ExpectTotalCount(
      "ChromeOS.CertProvisioning.Result.Dynamic.User", 2);
}

// Checks that the worker removes a key when an error occurs after the key was
// registered.
// This test covers the case when EC keys are used.
TEST_F(CertProvisioningWorkerDynamicTest, RemoveRegisteredKeyEcKeys) {
  base::HistogramTester histogram_tester;

  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const std::string listener_type = MakeInvalidationListenerType(process_id);
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  MockCertProvisioningInvalidator* mock_invalidator = nullptr;
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_,
      MakeInvalidator(&mock_invalidator), GetStateChangeCallback(),
      GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_CALL(*mock_invalidator, Register(listener_type, _)).Times(1);

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));

    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_FAIL(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_CALL(*mock_invalidator, Unregister()).Times(1);

    EXPECT_CALL(*platform_keys_service_,
                RemoveKey(TokenId::kUser,
                          /*public_key_spki_der=*/GetPublicKeyBin(KeyType::kEc),
                          /*callback=*/_))
        .Times(1)
        .WillOnce(RunOnceCallback<2>(Status::kSuccess));
  }

  worker.DoStep();
  FastForwardBy(base::Seconds(1));

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);

  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Result.Dynamic.User",
      CertProvisioningWorkerState::kFailed, 1);
  histogram_tester.ExpectBucketCount(
      "ChromeOS.CertProvisioning.Result.Dynamic.User",
      CertProvisioningWorkerState::kKeyRegistered, 1);
  histogram_tester.ExpectTotalCount(
      "ChromeOS.CertProvisioning.Result.Dynamic.User", 2);
}

TEST_F(CertProvisioningWorkerDynamicTest, ResetWorkerRsaKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  worker.MarkWorkerForReset();
  ASSERT_EQ(worker.IsWorkerMarkedForReset(), true);
}

TEST_F(CertProvisioningWorkerDynamicTest, ResetWorkerEcKeys) {
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, CertScope::kUser, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));
  CertProvisioningWorkerDynamic worker(
      process_id, CertScope::kUser, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  worker.MarkWorkerForReset();
  ASSERT_EQ(worker.IsWorkerMarkedForReset(), true);
}

class PrefServiceObserver {
 public:
  PrefServiceObserver(PrefService* service, const char* pref_name)
      : service_(service), pref_name_(pref_name) {
    pref_change_registrar_.Init(service);
    pref_change_registrar_.Add(
        pref_name, base::BindRepeating(&PrefServiceObserver::OnPrefsChange,
                                       weak_factory_.GetWeakPtr()));
  }

  void OnPrefsChange() {
    const base::Value& pref_value = service_->GetValue(pref_name_);
    OnPrefValueUpdated(pref_value);
  }

  // Allows to add expectations about preference changes and verify new values.
  MOCK_METHOD(void, OnPrefValueUpdated, (const base::Value& value));

 private:
  raw_ptr<PrefService> service_ = nullptr;
  const char* pref_name_ = nullptr;
  PrefChangeRegistrar pref_change_registrar_;
  base::WeakPtrFactory<PrefServiceObserver> weak_factory_{this};
};

TEST_F(CertProvisioningWorkerDynamicTest, SerializationSuccessRsaKeys) {
  const base::TimeDelta kRenewalPeriod = base::Seconds(1200300);
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kRenewalPeriod, ProtocolVersion::kDynamic);
  const CertScope kCertScope = CertScope::kUser;
  const std::string process_id = GenerateCertProvisioningId();
  const std::string listener_type = MakeInvalidationListenerType(process_id);
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, kCertScope, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  std::unique_ptr<MockCertProvisioningInvalidator> mock_invalidator_obj;
  MockCertProvisioningInvalidator* mock_invalidator = nullptr;

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  std::unique_ptr<CertProvisioningWorker> worker =
      CertProvisioningWorkerFactory::Get()->Create(
          process_id, kCertScope, GetProfile(), &testing_pref_service_,
          cert_profile, &cert_provisioning_client_,
          MakeInvalidator(&mock_invalidator), GetStateChangeCallback(),
          GetResultCallback());

  StrictMock<PrefServiceObserver> pref_observer(
      &testing_pref_service_, GetPrefNameForSerialization(kCertScope));
  base::DictValue pref_val;

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));

  // Prepare key, send Start request.
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_CALL(*mock_invalidator, Register(listener_type, _)).Times(1);

    // Serialized in kReadyForNextOperation = 12 state
    pref_val = ParseJsonDict(base::StringPrintf(
        R"({
          "cert_profile_1": {
            "cert_profile": {
              "policy_version": "cert_profile_version_1",
              "name": "Certificate Profile 1",
              "profile_id": "cert_profile_1",
              "va_enabled": true,
              "renewal_period": 1200300,
              "protocol_version": 2,
              "key_type": 1
            },
            "cert_scope": 0,
            "key_location": 1,
            "process_id": "%s",
            "attempted_va_challenge": false,
            "attempted_proof_of_possession": false,
            "proof_of_possession_signature": "",
            "public_key": "%s",
            "state": 12
          }
        })",
        process_id.c_str(), kPublicKeyRsaBase64));

    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);
    EXPECT_GET_NEXT_INSTRUCTION_NO_OP(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_));

    worker->DoStep();
  }

  // Recreate worker.
  {
    testing::InSequence seq;

    mock_invalidator_obj = MakeInvalidator(&mock_invalidator);
    EXPECT_CALL(*mock_invalidator, Register(listener_type, _)).Times(1);

    mock_tpm_challenge_key = PrepareTpmChallengeKey();

    EXPECT_CALL(*mock_tpm_challenge_key,
                RestorePreparedKeyState(
                    ::attestation::ENTERPRISE_USER,
                    /*will_register_key=*/true, ::attestation::KEY_TYPE_RSA,
                    GetKeyName(kCertProfileId), GetPublicKey(KeyType::kRsa),
                    /*profile=*/_))
        .Times(1);

    worker = CertProvisioningWorkerFactory::Get()->Deserialize(
        kCertScope, GetProfile(), &testing_pref_service_,
        *pref_val.FindDict(kCertProfileId), &cert_provisioning_client_,
        std::move(mock_invalidator_obj), GetStateChangeCallback(),
        GetResultCallback());
  }

  // Retry GetNextInstruction, receive response, try sign challenge.
  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    pref_val = ParseJsonDict("{}");
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));

    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kRsa)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kRsa),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    pref_val = ParseJsonDict(base::StringPrintf(
        R"({
          "cert_profile_1": {
            "cert_profile": {
              "policy_version": "cert_profile_version_1",
              "name": "Certificate Profile 1",
              "profile_id": "cert_profile_1",
              "va_enabled": true,
              "renewal_period": 1200300,
              "protocol_version": 2,
              "key_type": 1
            },
            "cert_scope": 0,
            "key_location": 2,
            "process_id": "%s",
            "attempted_va_challenge": true,
            "attempted_proof_of_possession": false,
            "proof_of_possession_signature": "",
            "public_key": "%s",
            "state": 12
          }
        })",
        process_id.c_str(), kPublicKeyRsaBase64));
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    worker->DoStep();
  }

  // Recreate worker.
  {
    testing::InSequence seq;

    mock_invalidator_obj = MakeInvalidator(&mock_invalidator);
    EXPECT_CALL(*mock_invalidator, Register(listener_type, _)).Times(1);

    worker = CertProvisioningWorkerFactory::Get()->Deserialize(
        kCertScope, GetProfile(), &testing_pref_service_,
        *pref_val.FindDict(kCertProfileId), &cert_provisioning_client_,
        std::move(mock_invalidator_obj), GetStateChangeCallback(),
        GetResultCallback());
  }

  // Retry StartOrContinue request, receive proof of possession instruction.
  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kRsa));

    pref_val = ParseJsonDict("{}");
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    EXPECT_SIGN_RSAPKC1_RAW_OK(
        SignRSAPKCS1Raw(::testing::Optional(TokenId::kUser), GetDataToSignBin(),
                        GetPublicKeyBin(KeyType::kRsa), /*callback=*/_));

    pref_val = ParseJsonDict(base::StringPrintf(
        R"({
          "cert_profile_1": {
            "cert_profile": {
              "policy_version": "cert_profile_version_1",
              "name": "Certificate Profile 1",
              "profile_id": "cert_profile_1",
              "va_enabled": true,
              "renewal_period": 1200300,
              "protocol_version": 2,
              "key_type": 1
            },
            "cert_scope": 0,
            "key_location": 2,
            "process_id": "%s",
            "attempted_va_challenge": true,
            "attempted_proof_of_possession": true,
            "proof_of_possession_signature": "%s",
            "public_key": "%s",
            "state": 6
          }
        })",
        process_id.c_str(), kSignatureBase64, kPublicKeyRsaBase64));
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetSignatureStr(),
                                /*callback=*/_),
        base::unexpected(
            DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
    worker->DoStep();
  }

  // Recreate worker.
  {
    testing::InSequence seq;

    mock_invalidator_obj = MakeInvalidator(&mock_invalidator);
    EXPECT_CALL(*mock_invalidator, Register(listener_type, _)).Times(1);

    worker = CertProvisioningWorkerFactory::Get()->Deserialize(
        kCertScope, GetProfile(), &testing_pref_service_,
        *pref_val.FindDict(kCertProfileId), &cert_provisioning_client_,
        std::move(mock_invalidator_obj), GetStateChangeCallback(),
        GetResultCallback());
  }

  // Retry the UploadProofOfPossession request with the serialized signature.
  {
    testing::InSequence seq;
    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetSignatureStr(),
                                /*callback=*/_),
        NoDataResultOk());

    pref_val = ParseJsonDict(base::StringPrintf(
        R"({
          "cert_profile_1": {
            "cert_profile": {
              "policy_version": "cert_profile_version_1",
              "name": "Certificate Profile 1",
              "profile_id": "cert_profile_1",
              "va_enabled": true,
              "renewal_period": 1200300,
              "protocol_version": 2,
              "key_type": 1
            },
            "cert_scope": 0,
            "key_location": 2,
            "process_id": "%s",
            "attempted_va_challenge": true,
            "attempted_proof_of_possession": true,
            "proof_of_possession_signature": "",
            "public_key": "%s",
            "state": 12
          }
        })",
        process_id.c_str(), kPublicKeyRsaBase64));
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    worker->DoStep();
  }

  // Recreate worker.
  {
    testing::InSequence seq;

    mock_invalidator_obj = MakeInvalidator(&mock_invalidator);
    EXPECT_CALL(*mock_invalidator, Register(listener_type, _)).Times(1);

    worker = CertProvisioningWorkerFactory::Get()->Deserialize(
        kCertScope, GetProfile(), &testing_pref_service_,
        *pref_val.FindDict(kCertProfileId), &cert_provisioning_client_,
        std::move(mock_invalidator_obj), GetStateChangeCallback(),
        GetResultCallback());
  }

  // Retry GetNextInstruction request, receive import certificate instruction.
  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeRsaCertificate));

    pref_val = ParseJsonDict("{}");
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));

    pref_val = ParseJsonDict("{}");
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);
    EXPECT_CALL(*mock_invalidator, Unregister()).Times(1);

    worker->DoStep();

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }
}

TEST_F(CertProvisioningWorkerDynamicTest, SerializationSuccessEcKeys) {
  const base::TimeDelta kRenewalPeriod = base::Seconds(1200300);
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kRenewalPeriod, ProtocolVersion::kDynamic);
  const CertScope kCertScope = CertScope::kUser;
  const std::string process_id = GenerateCertProvisioningId();
  const std::string listener_type = MakeInvalidationListenerType(process_id);
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, kCertScope, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  std::unique_ptr<MockCertProvisioningInvalidator> mock_invalidator_obj;
  MockCertProvisioningInvalidator* mock_invalidator = nullptr;

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  std::unique_ptr<CertProvisioningWorker> worker =
      CertProvisioningWorkerFactory::Get()->Create(
          process_id, kCertScope, GetProfile(), &testing_pref_service_,
          cert_profile, &cert_provisioning_client_,
          MakeInvalidator(&mock_invalidator), GetStateChangeCallback(),
          GetResultCallback());

  StrictMock<PrefServiceObserver> pref_observer(
      &testing_pref_service_, GetPrefNameForSerialization(kCertScope));
  base::DictValue pref_val;

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));

  // Prepare key, send Start request.
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_CALL(*mock_invalidator, Register(listener_type, _)).Times(1);

    // Serialized in kReadyForNextOperation = 12 state
    pref_val = ParseJsonDict(base::StringPrintf(
        R"({
          "cert_profile_1": {
            "cert_profile": {
              "policy_version": "cert_profile_version_1",
              "name": "Certificate Profile 1",
              "profile_id": "cert_profile_1",
              "va_enabled": true,
              "renewal_period": 1200300,
              "protocol_version": 2,
              "key_type": 2
            },
            "cert_scope": 0,
            "key_location": 1,
            "process_id": "%s",
            "attempted_va_challenge": false,
            "attempted_proof_of_possession": false,
            "proof_of_possession_signature": "",
            "public_key": "%s",
            "state": 12
          }
        })",
        process_id.c_str(), kPublicKeyEcBase64));

    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);
    EXPECT_GET_NEXT_INSTRUCTION_NO_OP(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_));

    worker->DoStep();
  }

  // Recreate worker.
  {
    testing::InSequence seq;

    mock_invalidator_obj = MakeInvalidator(&mock_invalidator);
    EXPECT_CALL(*mock_invalidator, Register(listener_type, _)).Times(1);

    mock_tpm_challenge_key = PrepareTpmChallengeKey();

    EXPECT_CALL(*mock_tpm_challenge_key,
                RestorePreparedKeyState(
                    ::attestation::ENTERPRISE_USER,
                    /*will_register_key=*/true, ::attestation::KEY_TYPE_ECC,
                    GetKeyName(kCertProfileId), GetPublicKey(KeyType::kEc),
                    /*profile=*/_))
        .Times(1);

    worker = CertProvisioningWorkerFactory::Get()->Deserialize(
        kCertScope, GetProfile(), &testing_pref_service_,
        *pref_val.FindDict(kCertProfileId), &cert_provisioning_client_,
        std::move(mock_invalidator_obj), GetStateChangeCallback(),
        GetResultCallback());
  }

  // Retry GetNextInstruction, receive response, try sign challenge.
  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionAuthorize());

    pref_val = ParseJsonDict("{}");
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key,
                             StartSignChallengeStep(kChallenge,
                                                    /*callback=*/_));

    EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep);

    EXPECT_CALL(*key_permissions_manager_,
                AllowKeyForUsage(/*callback=*/_, KeyUsage::kCorporate,
                                 GetPublicKeyBin(KeyType::kEc)));

    EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(
        SetAttributeForKey(TokenId::kUser, GetPublicKeyBin(KeyType::kEc),
                           KeyAttributeType::kCertificateProvisioningId,
                           GetCertProfileIdBin(), _));

    EXPECT_AUTHORIZE(
        Authorize(Eq(std::ref(provisioning_process)), kChallengeResponse,
                  /*callback=*/_),
        NoDataResultOk());

    pref_val = ParseJsonDict(base::StringPrintf(
        R"({
          "cert_profile_1": {
            "cert_profile": {
              "policy_version": "cert_profile_version_1",
              "name": "Certificate Profile 1",
              "profile_id": "cert_profile_1",
              "va_enabled": true,
              "renewal_period": 1200300,
              "protocol_version": 2,
              "key_type": 2
            },
            "cert_scope": 0,
            "key_location": 2,
            "process_id": "%s",
            "attempted_va_challenge": true,
            "attempted_proof_of_possession": false,
            "proof_of_possession_signature": "",
            "public_key": "%s",
            "state": 12
          }
        })",
        process_id.c_str(), kPublicKeyEcBase64));
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    worker->DoStep();
  }

  // Recreate worker.
  {
    testing::InSequence seq;

    mock_invalidator_obj = MakeInvalidator(&mock_invalidator);
    EXPECT_CALL(*mock_invalidator, Register(listener_type, _)).Times(1);

    worker = CertProvisioningWorkerFactory::Get()->Deserialize(
        kCertScope, GetProfile(), &testing_pref_service_,
        *pref_val.FindDict(kCertProfileId), &cert_provisioning_client_,
        std::move(mock_invalidator_obj), GetStateChangeCallback(),
        GetResultCallback());
  }

  // Retry StartOrContinue request, receive proof of possession instruction.
  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionProofOfPossession(KeyType::kEc));

    pref_val = ParseJsonDict("{}");
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    EXPECT_SIGN_ECDSA_OK(SignEcdsa(
        ::testing::Optional(TokenId::kUser), GetDataToSignBin(),
        GetPublicKeyBin(KeyType::kEc),
        chromeos::platform_keys::HASH_ALGORITHM_SHA256, /*callback=*/_));

    pref_val = ParseJsonDict(base::StringPrintf(
        R"({
          "cert_profile_1": {
            "cert_profile": {
              "policy_version": "cert_profile_version_1",
              "name": "Certificate Profile 1",
              "profile_id": "cert_profile_1",
              "va_enabled": true,
              "renewal_period": 1200300,
              "protocol_version": 2,
              "key_type": 2
            },
            "cert_scope": 0,
            "key_location": 2,
            "process_id": "%s",
            "attempted_va_challenge": true,
            "attempted_proof_of_possession": true,
            "proof_of_possession_signature": "%s",
            "public_key": "%s",
            "state": 6
          }
        })",
        process_id.c_str(), kEccSignatureAsn1Base64, kPublicKeyEcBase64));
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetEccSignatureAsn1Str(),
                                /*callback=*/_),
        base::unexpected(
            DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
    worker->DoStep();
  }

  // Recreate worker.
  {
    testing::InSequence seq;

    mock_invalidator_obj = MakeInvalidator(&mock_invalidator);
    EXPECT_CALL(*mock_invalidator, Register(listener_type, _)).Times(1);

    worker = CertProvisioningWorkerFactory::Get()->Deserialize(
        kCertScope, GetProfile(), &testing_pref_service_,
        *pref_val.FindDict(kCertProfileId), &cert_provisioning_client_,
        std::move(mock_invalidator_obj), GetStateChangeCallback(),
        GetResultCallback());
  }

  // Retry the UploadProofOfPossession request with the serialized signature.
  {
    testing::InSequence seq;
    EXPECT_UPLOAD_PROOF_OF_POSSESSION(
        UploadProofOfPossession(Eq(std::ref(provisioning_process)),
                                GetEccSignatureAsn1Str(),
                                /*callback=*/_),
        NoDataResultOk());

    pref_val = ParseJsonDict(base::StringPrintf(
        R"({
          "cert_profile_1": {
            "cert_profile": {
              "policy_version": "cert_profile_version_1",
              "name": "Certificate Profile 1",
              "profile_id": "cert_profile_1",
              "va_enabled": true,
              "renewal_period": 1200300,
              "protocol_version": 2,
              "key_type": 2
            },
            "cert_scope": 0,
            "key_location": 2,
            "process_id": "%s",
            "attempted_va_challenge": true,
            "attempted_proof_of_possession": true,
            "proof_of_possession_signature": "",
            "public_key": "%s",
            "state": 12
          }
        })",
        process_id.c_str(), kPublicKeyEcBase64));
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    worker->DoStep();
  }

  // Recreate worker.
  {
    testing::InSequence seq;

    mock_invalidator_obj = MakeInvalidator(&mock_invalidator);
    EXPECT_CALL(*mock_invalidator, Register(listener_type, _)).Times(1);

    worker = CertProvisioningWorkerFactory::Get()->Deserialize(
        kCertScope, GetProfile(), &testing_pref_service_,
        *pref_val.FindDict(kCertProfileId), &cert_provisioning_client_,
        std::move(mock_invalidator_obj), GetStateChangeCallback(),
        GetResultCallback());
  }

  // Retry GetNextInstruction request, receive import certificate instruction.
  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        NextInstructionImportCertificate(kFakeEcCertificate));

    pref_val = ParseJsonDict("{}");
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    EXPECT_IMPORT_CERTIFICATE_OK(
        ImportCertificate(TokenId::kUser, /*certificate=*/_, /*callback=*/_));

    pref_val = ParseJsonDict("{}");
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);
    EXPECT_CALL(*mock_invalidator, Unregister()).Times(1);

    worker->DoStep();

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kSucceeded);
  }
}

TEST_F(CertProvisioningWorkerDynamicTest, SerializationOnFailureRsaKeys) {
  const CertScope kCertScope = CertScope::kUser;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, kCertScope, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  auto worker = CertProvisioningWorkerFactory::Get()->Create(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  PrefServiceObserver pref_observer(&testing_pref_service_,
                                    GetPrefNameForSerialization(kCertScope));
  base::DictValue pref_val;

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_RSA,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    // Serialized in kReadyForNextOperation = 12 state
    pref_val = ParseJsonDict(base::StringPrintf(
        R"({
          "cert_profile_1": {
            "cert_profile": {
              "policy_version": "cert_profile_version_1",
              "name": "Certificate Profile 1",
              "profile_id": "cert_profile_1",
              "va_enabled": true,
              "protocol_version": 2,
              "key_type": 1
            },
            "cert_scope": 0,
            "key_location": 1,
            "process_id": "%s",
            "attempted_va_challenge": false,
            "attempted_proof_of_possession": false,
            "proof_of_possession_signature": "",
            "public_key": "%s",
            "state": 12
          }
        })",
        process_id.c_str(), kPublicKeyRsaBase64));

    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);
    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(BackendError(
            em::CertProvBackendError::BAD_ADAPTER_CERTIFICATE_RECEIVED)));

    pref_val = ParseJsonDict("{}");
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);
  }

  worker->DoStep();
  FastForwardBy(base::Seconds(1));

  VerifyDeleteKeyCalledOnce(kCertScope);

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);
}

TEST_F(CertProvisioningWorkerDynamicTest, SerializationOnFailureEcKeys) {
  const CertScope kCertScope = CertScope::kUser;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, kCertScope, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  auto worker = CertProvisioningWorkerFactory::Get()->Create(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  PrefServiceObserver pref_observer(&testing_pref_service_,
                                    GetPrefNameForSerialization(kCertScope));
  base::DictValue pref_val;

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key,
                          StartPrepareKeyStep(::attestation::ENTERPRISE_USER,
                                              /*will_register_key=*/true,
                                              ::attestation::KEY_TYPE_ECC,
                                              GetKeyName(kCertProfileId),
                                              /*profile=*/_,
                                              /*callback=*/_, /*signals=*/_),
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    // Serialized in kReadyForNextOperation = 12 state
    pref_val = ParseJsonDict(base::StringPrintf(
        R"({
          "cert_profile_1": {
            "cert_profile": {
              "policy_version": "cert_profile_version_1",
              "name": "Certificate Profile 1",
              "profile_id": "cert_profile_1",
              "va_enabled": true,
              "protocol_version": 2,
              "key_type": 2
            },
            "cert_scope": 0,
            "key_location": 1,
            "process_id": "%s",
            "attempted_va_challenge": false,
            "attempted_proof_of_possession": false,
            "proof_of_possession_signature": "",
            "public_key": "%s",
            "state": 12
          }
        })",
        process_id.c_str(), kPublicKeyEcBase64));

    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);
    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(BackendError(
            em::CertProvBackendError::BAD_ADAPTER_CERTIFICATE_RECEIVED)));

    pref_val = ParseJsonDict("{}");
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);
  }

  worker->DoStep();
  FastForwardBy(base::Seconds(1));

  VerifyDeleteKeyCalledOnce(kCertScope);

  EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
  EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
            CertProvisioningWorkerState::kFailed);
}

TEST_F(CertProvisioningWorkerDynamicTest, InformationalGettersRsaKeys) {
  const CertScope kCertScope = CertScope::kUser;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, kCertScope, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key, StartPrepareKeyStep,
                          KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    EXPECT_EQ(worker.GetPreviousState(),
              CertProvisioningWorkerState::kKeypairGenerated);
    EXPECT_EQ(worker.GetCertProfile(), cert_profile);
    EXPECT_EQ(worker.GetPublicKey(), GetPublicKeyBin(KeyType::kRsa));
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(BackendError(em::CertProvBackendError::CA_FAILURE)));

    worker.DoStep();
    FastForwardBy(base::Seconds(1));

    VerifyDeleteKeyCalledOnce(kCertScope);

    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kFailed);
    EXPECT_EQ(worker.GetPreviousState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    EXPECT_EQ(worker.GetCertProfile(), cert_profile);
    EXPECT_EQ(worker.GetPublicKey(), GetPublicKeyBin(KeyType::kRsa));

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kFailed);
  }
}

TEST_F(CertProvisioningWorkerDynamicTest, InformationalGettersEcKeys) {
  const CertScope kCertScope = CertScope::kUser;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, kCertScope, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  CertProvisioningWorkerDynamic worker(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key, StartPrepareKeyStep,
                          KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(InstructionNotYetAvailable()));

    worker.DoStep();
    EXPECT_EQ(worker.GetState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    EXPECT_EQ(worker.GetPreviousState(),
              CertProvisioningWorkerState::kKeypairGenerated);
    EXPECT_EQ(worker.GetCertProfile(), cert_profile);
    EXPECT_EQ(worker.GetPublicKey(), GetPublicKeyBin(KeyType::kEc));
  }

  {
    testing::InSequence seq;

    EXPECT_GET_NEXT_INSTRUCTION(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_),
        base::unexpected(BackendError(em::CertProvBackendError::CA_FAILURE)));

    worker.DoStep();
    FastForwardBy(base::Seconds(1));

    VerifyDeleteKeyCalledOnce(kCertScope);

    EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kFailed);
    EXPECT_EQ(worker.GetPreviousState(),
              CertProvisioningWorkerState::kReadyForNextOperation);
    EXPECT_EQ(worker.GetCertProfile(), cert_profile);
    EXPECT_EQ(worker.GetPublicKey(), GetPublicKeyBin(KeyType::kEc));

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kFailed);
  }
}

TEST_F(CertProvisioningWorkerDynamicTest, CancelDeviceWorkerRsaKeys) {
  base::HistogramTester histogram_tester;

  const CertScope kCertScope = CertScope::kDevice;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kRsa,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, kCertScope, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kRsa));

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  auto worker = CertProvisioningWorkerFactory::Get()->Create(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  PrefServiceObserver pref_observer(&testing_pref_service_,
                                    GetPrefNameForSerialization(kCertScope));
  base::DictValue pref_val;

  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(
        *mock_tpm_challenge_key,
        StartPrepareKeyStep(::attestation::ENTERPRISE_MACHINE,
                            /*will_register_key=*/true,
                            ::attestation::KEY_TYPE_RSA,
                            /*key_name=*/GetKeyName(kCertProfileId),
                            /*profile=*/_,
                            /*callback=*/_, /*signals=*/_),
        KeyType::kRsa);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    pref_val = ParseJsonDict(base::StringPrintf(
        R"({
          "cert_profile_1": {
            "cert_profile": {
              "policy_version": "cert_profile_version_1",
              "name": "Certificate Profile 1",
              "profile_id": "cert_profile_1",
              "va_enabled": true,
              "protocol_version": 2,
              "key_type": 1
            },
            "cert_scope": 1,
            "key_location": 1,
            "process_id": "%s",
            "attempted_va_challenge": false,
            "attempted_proof_of_possession": false,
            "proof_of_possession_signature": "",
            "public_key": "%s",
            "state": 12
          }
        })",
        process_id.c_str(), kPublicKeyRsaBase64));
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    EXPECT_GET_NEXT_INSTRUCTION_NO_OP(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_));

    worker->DoStep();
  }

  {
    pref_val = ParseJsonDict("{}");
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    worker->Stop(CertProvisioningWorkerState::kCanceled);

    FastForwardBy(base::Seconds(1));

    VerifyDeleteKeyCalledOnce(kCertScope);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kCanceled);
  }

  histogram_tester.ExpectUniqueSample(
      "ChromeOS.CertProvisioning.Result.Dynamic.Device",
      CertProvisioningWorkerState::kCanceled, 1);
}

TEST_F(CertProvisioningWorkerDynamicTest, CancelDeviceWorkerEcKeys) {
  base::HistogramTester histogram_tester;

  const CertScope kCertScope = CertScope::kDevice;
  const CertProfile cert_profile(
      kCertProfileId, kCertProfileName, kCertProfileVersion, KeyType::kEc,
      /*is_va_enabled=*/true, kCertProfileRenewalPeriod,
      ProtocolVersion::kDynamic);
  const std::string process_id = GenerateCertProvisioningId();
  const CertProvisioningClient::ProvisioningProcess provisioning_process(
      process_id, kCertScope, kCertProfileId, kCertProfileVersion,
      GetPublicKeyBin(KeyType::kEc));

  EXPECT_CALL(state_change_callback_observer_, StateChangeCallback)
      .Times(AtLeast(1));
  MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey();
  auto worker = CertProvisioningWorkerFactory::Get()->Create(
      process_id, kCertScope, GetProfile(), &testing_pref_service_,
      cert_profile, &cert_provisioning_client_, MakeInvalidator(),
      GetStateChangeCallback(), GetResultCallback());

  PrefServiceObserver pref_observer(&testing_pref_service_,
                                    GetPrefNameForSerialization(kCertScope));
  base::DictValue pref_val;

  {
    testing::InSequence seq;

    EXPECT_PREPARE_KEY_OK(
        *mock_tpm_challenge_key,
        StartPrepareKeyStep(::attestation::ENTERPRISE_MACHINE,
                            /*will_register_key=*/true,
                            ::attestation::KEY_TYPE_ECC,
                            /*key_name=*/GetKeyName(kCertProfileId),
                            /*profile=*/_,
                            /*callback=*/_, /*signals=*/_),
        KeyType::kEc);

    EXPECT_START(Start(Eq(std::ref(provisioning_process)), /*callback=*/_),
                 StartResultOk());

    pref_val = ParseJsonDict(base::StringPrintf(
        R"({
          "cert_profile_1": {
            "cert_profile": {
              "policy_version": "cert_profile_version_1",
              "name": "Certificate Profile 1",
              "profile_id": "cert_profile_1",
              "va_enabled": true,
              "protocol_version": 2,
              "key_type": 2
            },
            "cert_scope": 1,
            "key_location": 1,
            "process_id": "%s",
            "attempted_va_challenge": false,
            "attempted_proof_of_possession": false,
            "proof_of_possession_signature": "",
            "public_key": "%s",
            "state": 12
          }
        })",
        process_id.c_str(), kPublicKeyEcBase64));
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    EXPECT_GET_NEXT_INSTRUCTION_NO_OP(
        GetNextInstruction(Eq(std::ref(provisioning_process)), /*callback=*/_));

    worker->DoStep();
  }

  {
    pref_val = ParseJsonDict("{}");
    EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);

    worker->Stop(CertProvisioningWorkerState::kCanceled);

    FastForwardBy(base::Seconds(1));

    VerifyDeleteKeyCalledOnce(kCertScope);

    EXPECT_EQ(callback_observer_.Get<CertProfile>(), cert_profile);
    EXPECT_EQ(callback_observer_.Get<CertProvisioningWorkerState>(),
              CertProvisioningWorkerState::kCanceled);
  }

  histogram_tester.ExpectUniqueSample(
      "ChromeOS.CertProvisioning.Result.Dynamic.Device",
      CertProvisioningWorkerState::kCanceled, 1);
}

}  // namespace
}  // namespace ash::cert_provisioning
