| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h" |
| |
| #include "base/callback.h" |
| #include "base/json/json_string_value_serializer.h" |
| #include "base/json/json_writer.h" |
| #include "base/test/gmock_callback_support.h" |
| #include "base/test/values_test_util.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/chromeos/attestation/mock_tpm_challenge_key_subtle.h" |
| #include "chrome/browser/chromeos/attestation/tpm_challenge_key_subtle.h" |
| #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h" |
| #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_test_helpers.h" |
| #include "chrome/browser/chromeos/cert_provisioning/mock_cert_provisioning_invalidator.h" |
| #include "chrome/browser/chromeos/platform_keys/mock_platform_keys_service.h" |
| #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h" |
| #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h" |
| #include "components/policy/core/common/cloud/mock_cloud_policy_client.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 em = enterprise_management; |
| |
| using base::TimeDelta; |
| using base::test::IsJson; |
| using base::test::ParseJson; |
| using base::test::RunOnceCallback; |
| using chromeos::attestation::MockTpmChallengeKeySubtle; |
| using testing::_; |
| using testing::Mock; |
| using testing::StrictMock; |
| |
| namespace chromeos { |
| namespace cert_provisioning { |
| namespace { |
| |
| // Generated by chrome/test/data/policy/test_certs/create_test_certs.sh |
| const char kFakeCertificate[] = 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-----)"; |
| |
| const char kCertProfileId[] = "cert_profile_1"; |
| const char kCertProfileVersion[] = "cert_profile_version_1"; |
| // Prefix + certificate profile name. |
| const char kCertScopeStrUser[] = "google/chromeos/user"; |
| const char kCertScopeStrDevice[] = "google/chromeos/device"; |
| const char kPublicKey[] = "fake_public_key_1"; |
| const char kInvalidationTopic[] = "fake_invalidation_topic_1"; |
| const char kDataToSign[] = "fake_data_to_sign_1"; |
| const em::HashingAlgorithm kProtoHashAlgo = em::HashingAlgorithm::SHA256; |
| const platform_keys::HashAlgorithm kPkHashAlgo = |
| platform_keys::HashAlgorithm::HASH_ALGORITHM_SHA256; |
| const char kChallenge[] = "fake_va_challenge_1"; |
| const char kChallengeResponse[] = "fake_va_challenge_response_1"; |
| const char kSignature[] = "fake_signature_1"; |
| |
| // 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 CertProvisioningWorkerTest 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) \ |
| { \ |
| auto public_key_result = \ |
| attestation::TpmChallengeKeyResult::MakePublicKey(kPublicKey); \ |
| EXPECT_CALL((MOCK_TPM_CHALLENGE_KEY), PREPARE_KEY_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<4>(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<2>(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_CSR_OK(START_CSR_FUNC) \ |
| { \ |
| EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<4>( \ |
| policy::DeviceManagementStatus::DM_STATUS_SUCCESS, \ |
| /*response_error=*/base::nullopt, \ |
| /*try_again_later_ms=*/base::nullopt, kInvalidationTopic, \ |
| kChallenge, kProtoHashAlgo, kDataToSign)); \ |
| } |
| |
| #define EXPECT_START_CSR_OK_WITHOUT_VA(START_CSR_FUNC) \ |
| { \ |
| EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<4>( \ |
| policy::DeviceManagementStatus::DM_STATUS_SUCCESS, \ |
| /*response_error=*/base::nullopt, \ |
| /*try_again_later_ms=*/base::nullopt, kInvalidationTopic, \ |
| /*va_challenge=*/"", kProtoHashAlgo, kDataToSign)); \ |
| } |
| |
| #define EXPECT_START_CSR_TRY_LATER(START_CSR_FUNC, DELAY_MS) \ |
| { \ |
| EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<4>( \ |
| policy::DeviceManagementStatus::DM_STATUS_SUCCESS, \ |
| /*response_error=*/base::nullopt, \ |
| /*try_again_later_ms=*/(DELAY_MS), /*invalidation_topic=*/"", \ |
| /*va_challenge=*/"", \ |
| enterprise_management::HashingAlgorithm:: \ |
| HASHING_ALGORITHM_UNSPECIFIED, \ |
| /*data_to_sign=*/"")); \ |
| } |
| |
| #define EXPECT_START_CSR_INVALID_REQUEST(START_CSR_FUNC) \ |
| { \ |
| EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<4>( \ |
| policy::DeviceManagementStatus::DM_STATUS_REQUEST_INVALID, \ |
| /*response_error=*/base::nullopt, \ |
| /*try_again_later_ms=*/base::nullopt, /*invalidation_topic=*/"", \ |
| /*va_challenge=*/"", \ |
| enterprise_management::HashingAlgorithm:: \ |
| HASHING_ALGORITHM_UNSPECIFIED, \ |
| /*data_to_sign=*/"")); \ |
| } |
| |
| #define EXPECT_START_CSR_CA_ERROR(START_CSR_FUNC) \ |
| { \ |
| EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<4>( \ |
| policy::DeviceManagementStatus::DM_STATUS_SUCCESS, \ |
| /*response_error=*/CertProvisioningResponseError::CA_ERROR, \ |
| /*try_again_later_ms=*/base::nullopt, /*invalidation_topic=*/"", \ |
| /*va_challenge=*/"", \ |
| enterprise_management::HashingAlgorithm:: \ |
| HASHING_ALGORITHM_UNSPECIFIED, \ |
| /*data_to_sign=*/"")); \ |
| } |
| |
| #define EXPECT_START_CSR_TEMPORARY_UNAVAILABLE(START_CSR_FUNC) \ |
| { \ |
| EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<4>( \ |
| policy::DeviceManagementStatus::DM_STATUS_TEMPORARY_UNAVAILABLE, \ |
| /*response_error=*/base::nullopt, \ |
| /*try_again_later_ms=*/base::nullopt, /*invalidation_topic=*/"", \ |
| /*va_challenge=*/"", \ |
| enterprise_management::HashingAlgorithm:: \ |
| HASHING_ALGORITHM_UNSPECIFIED, \ |
| /*data_to_sign=*/"")); \ |
| } |
| |
| #define EXPECT_START_CSR_INCONSISTENT_DATA(START_CSR_FUNC) \ |
| { \ |
| EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<4>( \ |
| policy::DeviceManagementStatus:: \ |
| DM_STATUS_SUCCESS, /*response_error=*/ \ |
| CertProvisioningResponseError::INCONSISTENT_DATA, \ |
| /*try_again_later_ms=*/base::nullopt, /*invalidation_topic=*/"", \ |
| /*va_challenge=*/"", \ |
| enterprise_management::HashingAlgorithm:: \ |
| HASHING_ALGORITHM_UNSPECIFIED, \ |
| /*data_to_sign=*/"")); \ |
| } |
| |
| #define EXPECT_START_CSR_NO_OP(START_CSR_FUNC) \ |
| { EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC).Times(1); } |
| |
| #define EXPECT_FINISH_CSR_OK(FINISH_CSR_FUNC) \ |
| { \ |
| EXPECT_CALL(cloud_policy_client_, FINISH_CSR_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<6>( \ |
| policy::DeviceManagementStatus::DM_STATUS_SUCCESS, base::nullopt, \ |
| base::nullopt)); \ |
| } |
| |
| #define EXPECT_FINISH_CSR_TRY_LATER(FINISH_CSR_FUNC, DELAY_MS) \ |
| { \ |
| EXPECT_CALL(cloud_policy_client_, FINISH_CSR_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<6>( \ |
| policy::DeviceManagementStatus::DM_STATUS_SUCCESS, base::nullopt, \ |
| /*try_again_later_ms=*/(DELAY_MS))); \ |
| } |
| |
| #define EXPECT_DOWNLOAD_CERT_OK(DOWNLOAD_CERT_FUNC) \ |
| { \ |
| EXPECT_CALL(cloud_policy_client_, DOWNLOAD_CERT_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<4>( \ |
| policy::DeviceManagementStatus::DM_STATUS_SUCCESS, base::nullopt, \ |
| base::nullopt, kFakeCertificate)); \ |
| } |
| #define EXPECT_DOWNLOAD_CERT_TRY_LATER(DOWNLOAD_CERT_FUNC, DELAY_MS) \ |
| { \ |
| EXPECT_CALL(cloud_policy_client_, DOWNLOAD_CERT_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<4>( \ |
| policy::DeviceManagementStatus::DM_STATUS_SUCCESS, base::nullopt, \ |
| /*try_again_later_ms=*/(DELAY_MS), /*certificate=*/"")); \ |
| } |
| |
| #define EXPECT_DOWNLOAD_CERT_NO_OP(DOWNLOAD_CERT_FUNC) \ |
| { EXPECT_CALL(cloud_policy_client_, DOWNLOAD_CERT_FUNC).Times(1); } |
| |
| #define EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(SET_FUNC) \ |
| { \ |
| EXPECT_CALL(*platform_keys_service_, SET_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<4>(/*error_message=*/"")); \ |
| } |
| |
| #define EXPECT_SET_ATTRIBUTE_FOR_KEY_FAIL(SET_FUNC) \ |
| { \ |
| EXPECT_CALL(*platform_keys_service_, SET_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<4>(/*error_message=*/"Test error")); \ |
| } |
| |
| #define EXPECT_SIGN_RSAPKC1_DIGEST_OK(SIGN_FUNC) \ |
| { \ |
| EXPECT_CALL(*platform_keys_service_, SIGN_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<4>(kSignature, /*error_message=*/"")); \ |
| } |
| |
| #define EXPECT_SIGN_RSAPKC1_DIGEST_FAIL(SIGN_FUNC) \ |
| { \ |
| EXPECT_CALL(*platform_keys_service_, SIGN_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<4>(/*signature=*/"", \ |
| /*error_message=*/"Test error")); \ |
| } |
| |
| #define EXPECT_IMPORT_CERTIFICATE_OK(IMPORT_FUNC) \ |
| { \ |
| EXPECT_CALL(*platform_keys_service_, IMPORT_FUNC) \ |
| .Times(1) \ |
| .WillOnce(RunOnceCallback<2>(/*error_message=*/"")); \ |
| } |
| |
| class CallbackObserver { |
| public: |
| MOCK_METHOD(void, |
| Callback, |
| (const CertProfile& profile, CertProvisioningWorkerState state)); |
| }; |
| |
| class CertProvisioningWorkerTest : public ::testing::Test { |
| public: |
| CertProvisioningWorkerTest() { Init(); } |
| CertProvisioningWorkerTest(const CertProvisioningWorkerTest&) = delete; |
| CertProvisioningWorkerTest& operator=(const CertProvisioningWorkerTest&) = |
| delete; |
| ~CertProvisioningWorkerTest() override = default; |
| |
| void SetUp() override { |
| // There should not be any calls to callback before this expect is |
| // overridden. |
| EXPECT_CALL(callback_observer_, Callback).Times(0); |
| |
| RegisterProfilePrefs(testing_pref_service_.registry()); |
| RegisterLocalStatePrefs(testing_pref_service_.registry()); |
| } |
| |
| void TearDown() override { |
| EXPECT_FALSE( |
| attestation::TpmChallengeKeySubtleFactory::WillReturnTestingInstance()); |
| } |
| |
| 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_); |
| // Only explicitly expected removals are allowed. |
| EXPECT_CALL(*platform_keys_service_, RemoveCertificate).Times(0); |
| EXPECT_CALL(*platform_keys_service_, RemoveKey).Times(0); |
| } |
| |
| void FastForwardBy(TimeDelta delta) { |
| task_environment_.FastForwardBy(delta); |
| } |
| |
| // 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; |
| } |
| |
| CertProvisioningWorkerCallback GetCallback() { |
| return base::BindOnce(&CallbackObserver::Callback, |
| base::Unretained(&callback_observer_)); |
| } |
| |
| 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<CallbackObserver> callback_observer_; |
| StrictMock<SpyingFakeCryptohomeClient> fake_cryptohome_client_; |
| ProfileHelperForTesting profile_helper_for_testing_; |
| TestingPrefServiceSimple testing_pref_service_; |
| |
| policy::MockCloudPolicyClient cloud_policy_client_; |
| platform_keys::MockPlatformKeysService* platform_keys_service_ = nullptr; |
| }; |
| |
| // Checks that the worker makes all necessary requests to other modules during |
| // success scenario. |
| TEST_F(CertProvisioningWorkerTest, Success) { |
| CertProfile cert_profile{kCertProfileId, kCertProfileVersion}; |
| |
| MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey(); |
| MockCertProvisioningInvalidator* mock_invalidator = nullptr; |
| CertProvisioningWorkerImpl worker( |
| CertScope::kUser, GetProfile(), &testing_pref_service_, cert_profile, |
| &cloud_policy_client_, MakeInvalidator(&mock_invalidator), GetCallback()); |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_PREPARE_KEY_OK( |
| *mock_tpm_challenge_key, |
| StartPrepareKeyStep(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId), |
| /*profile=*/_, /*key_name_for_spkac=*/"", |
| /*callback=*/_)); |
| |
| EXPECT_START_CSR_OK(ClientCertProvisioningStartCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| |
| EXPECT_CALL(*mock_invalidator, Register(kInvalidationTopic, _)).Times(1); |
| |
| EXPECT_SIGN_CHALLENGE_OK( |
| *mock_tpm_challenge_key, |
| StartSignChallengeStep(kChallenge, /*include_signed_public_key=*/true, |
| /*callback=*/_)); |
| |
| EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep); |
| |
| EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(SetAttributeForKey( |
| platform_keys::kTokenIdUser, kPublicKey, |
| platform_keys::KeyAttributeType::CertificateProvisioningId, |
| kCertProfileId, _)); |
| |
| EXPECT_SIGN_RSAPKC1_DIGEST_OK( |
| SignRSAPKCS1Digest(platform_keys::kTokenIdUser, kDataToSign, kPublicKey, |
| kPkHashAlgo, /*callback=*/_)); |
| |
| EXPECT_FINISH_CSR_OK(ClientCertProvisioningFinishCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| kChallengeResponse, kSignature, /*callback=*/_)); |
| |
| EXPECT_DOWNLOAD_CERT_OK(ClientCertProvisioningDownloadCert( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| |
| EXPECT_IMPORT_CERTIFICATE_OK(ImportCertificate( |
| platform_keys::kTokenIdUser, /*certificate=*/_, /*callback=*/_)); |
| |
| EXPECT_CALL(*mock_invalidator, Unregister()).Times(1); |
| |
| EXPECT_CALL(callback_observer_, |
| Callback(cert_profile, CertProvisioningWorkerState::kSucceed)) |
| .Times(1); |
| } |
| |
| worker.DoStep(); |
| } |
| |
| // Checks that the worker makes all necessary requests to other modules during |
| // success scenario when VA challenge is not received. |
| TEST_F(CertProvisioningWorkerTest, NoVaSuccess) { |
| CertProfile cert_profile{kCertProfileId, kCertProfileVersion}; |
| |
| MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey(); |
| CertProvisioningWorkerImpl worker( |
| CertScope::kUser, GetProfile(), &testing_pref_service_, cert_profile, |
| &cloud_policy_client_, MakeInvalidator(), GetCallback()); |
| EXPECT_CALL(*mock_tpm_challenge_key, StartRegisterKeyStep).Times(0); |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_PREPARE_KEY_OK( |
| *mock_tpm_challenge_key, |
| StartPrepareKeyStep(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId), |
| /*profile=*/_, /*key_name_for_spkac=*/"", |
| /*callback=*/_)); |
| |
| EXPECT_START_CSR_OK_WITHOUT_VA(ClientCertProvisioningStartCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| |
| EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep); |
| |
| EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(SetAttributeForKey( |
| platform_keys::kTokenIdUser, kPublicKey, |
| platform_keys::KeyAttributeType::CertificateProvisioningId, |
| kCertProfileId, _)); |
| |
| EXPECT_SIGN_RSAPKC1_DIGEST_OK( |
| SignRSAPKCS1Digest(platform_keys::kTokenIdUser, kDataToSign, kPublicKey, |
| kPkHashAlgo, /*callback=*/_)); |
| |
| EXPECT_FINISH_CSR_OK(ClientCertProvisioningFinishCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*va_challenge_response=*/"", kSignature, /*callback=*/_)); |
| |
| EXPECT_DOWNLOAD_CERT_OK(ClientCertProvisioningDownloadCert( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| |
| EXPECT_IMPORT_CERTIFICATE_OK(ImportCertificate( |
| platform_keys::kTokenIdUser, /*certificate=*/_, /*callback=*/_)); |
| |
| EXPECT_CALL(callback_observer_, |
| Callback(cert_profile, CertProvisioningWorkerState::kSucceed)) |
| .Times(1); |
| } |
| |
| worker.DoStep(); |
| } |
| |
| // Checks that when the server returns try_again_later field, the worker will |
| // retry a request when it asked to continue the provisioning. |
| TEST_F(CertProvisioningWorkerTest, TryLaterManualRetry) { |
| CertProfile cert_profile{kCertProfileId, kCertProfileVersion}; |
| |
| MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey(); |
| CertProvisioningWorkerImpl worker( |
| CertScope::kDevice, GetProfile(), &testing_pref_service_, cert_profile, |
| &cloud_policy_client_, MakeInvalidator(), GetCallback()); |
| const TimeDelta delay = TimeDelta::FromSeconds(30); |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_PREPARE_KEY_OK( |
| *mock_tpm_challenge_key, |
| StartPrepareKeyStep( |
| attestation::AttestationKeyType::KEY_DEVICE, /*key_name=*/"", |
| /*profile=*/_, /*key_name_for_spkac=*/GetKeyName(kCertProfileId), |
| /*callback=*/_)); |
| |
| EXPECT_START_CSR_TRY_LATER( |
| ClientCertProvisioningStartCsr(kCertScopeStrDevice, kCertProfileId, |
| kCertProfileVersion, kPublicKey, |
| /*callback=*/_), |
| delay.InMilliseconds()); |
| |
| worker.DoStep(); |
| EXPECT_EQ(worker.GetState(), |
| CertProvisioningWorkerState::kKeypairGenerated); |
| } |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_START_CSR_OK(ClientCertProvisioningStartCsr( |
| kCertScopeStrDevice, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| |
| EXPECT_SIGN_CHALLENGE_OK( |
| *mock_tpm_challenge_key, |
| StartSignChallengeStep(kChallenge, /*include_signed_public_key=*/true, |
| /*callback=*/_)); |
| |
| EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep); |
| |
| EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(SetAttributeForKey( |
| platform_keys::kTokenIdSystem, kPublicKey, |
| platform_keys::KeyAttributeType::CertificateProvisioningId, |
| kCertProfileId, _)); |
| |
| EXPECT_SIGN_RSAPKC1_DIGEST_OK(SignRSAPKCS1Digest); |
| |
| EXPECT_FINISH_CSR_TRY_LATER( |
| ClientCertProvisioningFinishCsr( |
| kCertScopeStrDevice, kCertProfileId, kCertProfileVersion, |
| kPublicKey, kChallengeResponse, kSignature, /*callback=*/_), |
| delay.InMilliseconds()); |
| |
| worker.DoStep(); |
| EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSignCsrFinished); |
| } |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_FINISH_CSR_OK(ClientCertProvisioningFinishCsr( |
| kCertScopeStrDevice, kCertProfileId, kCertProfileVersion, kPublicKey, |
| kChallengeResponse, kSignature, /*callback=*/_)); |
| |
| EXPECT_DOWNLOAD_CERT_TRY_LATER( |
| ClientCertProvisioningDownloadCert(kCertScopeStrDevice, kCertProfileId, |
| kCertProfileVersion, kPublicKey, |
| /*callback=*/_), |
| delay.InMilliseconds()); |
| |
| worker.DoStep(); |
| EXPECT_EQ(worker.GetState(), |
| CertProvisioningWorkerState::kFinishCsrResponseReceived); |
| } |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_DOWNLOAD_CERT_OK(ClientCertProvisioningDownloadCert( |
| kCertScopeStrDevice, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| |
| EXPECT_IMPORT_CERTIFICATE_OK(ImportCertificate( |
| platform_keys::kTokenIdSystem, /*certificate=*/_, /*callback=*/_)); |
| |
| EXPECT_CALL(callback_observer_, |
| Callback(cert_profile, CertProvisioningWorkerState::kSucceed)) |
| .Times(1); |
| |
| worker.DoStep(); |
| EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceed); |
| } |
| } |
| |
| // Checks that when the server returns try_again_later field, the worker will |
| // automatically retry a request after some time. |
| TEST_F(CertProvisioningWorkerTest, TryLaterWait) { |
| CertProfile cert_profile{kCertProfileId, kCertProfileVersion}; |
| |
| MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey(); |
| CertProvisioningWorkerImpl worker( |
| CertScope::kUser, GetProfile(), &testing_pref_service_, cert_profile, |
| &cloud_policy_client_, MakeInvalidator(), GetCallback()); |
| |
| const TimeDelta start_csr_delay = TimeDelta::FromSeconds(30); |
| const TimeDelta finish_csr_delay = TimeDelta::FromSeconds(30); |
| const TimeDelta download_cert_server_delay = TimeDelta::FromMilliseconds(100); |
| const TimeDelta download_cert_real_delay = TimeDelta::FromSeconds(10); |
| const TimeDelta small_delay = TimeDelta::FromMilliseconds(500); |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_PREPARE_KEY_OK( |
| *mock_tpm_challenge_key, |
| StartPrepareKeyStep(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId), |
| /*profile=*/_, /*key_name_for_spkac=*/"", |
| /*callback=*/_)); |
| |
| EXPECT_START_CSR_TRY_LATER( |
| ClientCertProvisioningStartCsr(kCertScopeStrUser, kCertProfileId, |
| kCertProfileVersion, kPublicKey, |
| /*callback=*/_), |
| start_csr_delay.InMilliseconds()); |
| |
| worker.DoStep(); |
| EXPECT_EQ(worker.GetState(), |
| CertProvisioningWorkerState::kKeypairGenerated); |
| } |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_START_CSR_OK(ClientCertProvisioningStartCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| |
| EXPECT_SIGN_CHALLENGE_OK( |
| *mock_tpm_challenge_key, |
| StartSignChallengeStep(kChallenge, /*include_signed_public_key=*/true, |
| /*callback=*/_)); |
| |
| EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep); |
| |
| EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(SetAttributeForKey( |
| platform_keys::kTokenIdUser, kPublicKey, |
| platform_keys::KeyAttributeType::CertificateProvisioningId, |
| kCertProfileId, _)); |
| |
| EXPECT_SIGN_RSAPKC1_DIGEST_OK( |
| SignRSAPKCS1Digest(platform_keys::kTokenIdUser, kDataToSign, kPublicKey, |
| kPkHashAlgo, /*callback=*/_)); |
| |
| EXPECT_FINISH_CSR_TRY_LATER( |
| ClientCertProvisioningFinishCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| kChallengeResponse, kSignature, /*callback=*/_), |
| finish_csr_delay.InMilliseconds()); |
| |
| FastForwardBy(start_csr_delay + small_delay); |
| EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSignCsrFinished); |
| } |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_FINISH_CSR_OK(ClientCertProvisioningFinishCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| kChallengeResponse, kSignature, /*callback=*/_)); |
| |
| EXPECT_DOWNLOAD_CERT_TRY_LATER( |
| ClientCertProvisioningDownloadCert(kCertScopeStrUser, kCertProfileId, |
| kCertProfileVersion, kPublicKey, |
| /*callback=*/_), |
| download_cert_server_delay.InMilliseconds()); |
| |
| FastForwardBy(finish_csr_delay + small_delay); |
| EXPECT_EQ(worker.GetState(), |
| CertProvisioningWorkerState::kFinishCsrResponseReceived); |
| } |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_DOWNLOAD_CERT_OK(ClientCertProvisioningDownloadCert); |
| |
| EXPECT_IMPORT_CERTIFICATE_OK(ImportCertificate( |
| platform_keys::kTokenIdUser, /*certificate=*/_, /*callback=*/_)); |
| |
| FastForwardBy(small_delay); |
| // Check that minimum wait time is not too small even if the server |
| // has responded with a small one. |
| EXPECT_EQ(worker.GetState(), |
| CertProvisioningWorkerState::kFinishCsrResponseReceived); |
| |
| EXPECT_CALL(callback_observer_, |
| Callback(cert_profile, CertProvisioningWorkerState::kSucceed)) |
| .Times(1); |
| FastForwardBy(download_cert_real_delay + small_delay); |
| EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kSucceed); |
| } |
| } |
| |
| // Checks that when the server returns error status, the worker will enter an |
| // error state and stop the provisioning. |
| TEST_F(CertProvisioningWorkerTest, StatusErrorHandling) { |
| CertProfile cert_profile{kCertProfileId, kCertProfileVersion}; |
| |
| MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey(); |
| CertProvisioningWorkerImpl worker( |
| CertScope::kUser, GetProfile(), &testing_pref_service_, cert_profile, |
| &cloud_policy_client_, MakeInvalidator(), GetCallback()); |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_PREPARE_KEY_OK( |
| *mock_tpm_challenge_key, |
| StartPrepareKeyStep(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId), |
| /*profile=*/_, /*key_name_for_spkac=*/"", |
| /*callback=*/_)); |
| |
| EXPECT_START_CSR_INVALID_REQUEST(ClientCertProvisioningStartCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| |
| EXPECT_CALL( |
| fake_cryptohome_client_, |
| OnTpmAttestationDeleteKey(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId))) |
| .Times(1); |
| |
| EXPECT_CALL(callback_observer_, |
| Callback(cert_profile, CertProvisioningWorkerState::kFailed)) |
| .Times(1); |
| } |
| |
| worker.DoStep(); |
| FastForwardBy(TimeDelta::FromSeconds(1)); |
| } |
| |
| // Checks that when the server returns response error, the worker will enter an |
| // error state and stop the provisioning. Also check factory. |
| TEST_F(CertProvisioningWorkerTest, ResponseErrorHandling) { |
| CertProfile cert_profile{kCertProfileId, kCertProfileVersion}; |
| |
| MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey(); |
| auto worker = CertProvisioningWorkerFactory::Get()->Create( |
| CertScope::kUser, GetProfile(), &testing_pref_service_, cert_profile, |
| &cloud_policy_client_, MakeInvalidator(), GetCallback()); |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_PREPARE_KEY_OK( |
| *mock_tpm_challenge_key, |
| StartPrepareKeyStep(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId), |
| /*profile=*/_, /*key_name_for_spkac=*/"", |
| /*callback=*/_)); |
| |
| EXPECT_START_CSR_CA_ERROR(ClientCertProvisioningStartCsr); |
| |
| EXPECT_CALL( |
| fake_cryptohome_client_, |
| OnTpmAttestationDeleteKey(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId))) |
| .Times(1); |
| |
| EXPECT_CALL(callback_observer_, |
| Callback(cert_profile, CertProvisioningWorkerState::kFailed)) |
| .Times(1); |
| } |
| |
| worker->DoStep(); |
| FastForwardBy(TimeDelta::FromSeconds(1)); |
| } |
| |
| TEST_F(CertProvisioningWorkerTest, InconsistentDataErrorHandling) { |
| CertProfile cert_profile{kCertProfileId, kCertProfileVersion}; |
| |
| MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey(); |
| auto worker = CertProvisioningWorkerFactory::Get()->Create( |
| CertScope::kUser, GetProfile(), &testing_pref_service_, cert_profile, |
| &cloud_policy_client_, MakeInvalidator(), GetCallback()); |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_PREPARE_KEY_OK( |
| *mock_tpm_challenge_key, |
| StartPrepareKeyStep(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId), |
| /*profile=*/_, /*key_name_for_spkac=*/"", |
| /*callback=*/_)); |
| |
| EXPECT_START_CSR_INCONSISTENT_DATA(ClientCertProvisioningStartCsr); |
| |
| EXPECT_CALL( |
| fake_cryptohome_client_, |
| OnTpmAttestationDeleteKey(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId))) |
| .Times(1); |
| |
| EXPECT_CALL(callback_observer_, |
| Callback(cert_profile, |
| CertProvisioningWorkerState::kInconsistentDataError)) |
| .Times(1); |
| } |
| |
| worker->DoStep(); |
| FastForwardBy(TimeDelta::FromSeconds(1)); |
| } |
| |
| // Checks that when the server returns TEMPORARY_UNAVAILABLE status code, the |
| // worker will automatically retry a request using exponential backoff strategy. |
| TEST_F(CertProvisioningWorkerTest, BackoffStrategy) { |
| CertProfile cert_profile{kCertProfileId, kCertProfileVersion}; |
| |
| MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey(); |
| CertProvisioningWorkerImpl worker( |
| CertScope::kUser, GetProfile(), &testing_pref_service_, cert_profile, |
| &cloud_policy_client_, MakeInvalidator(), GetCallback()); |
| |
| TimeDelta next_delay = TimeDelta::FromSeconds(30); |
| const TimeDelta small_delay = TimeDelta::FromMilliseconds(500); |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_PREPARE_KEY_OK( |
| *mock_tpm_challenge_key, |
| StartPrepareKeyStep(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId), |
| /*profile=*/_, /*key_name_for_spkac=*/"", |
| /*callback=*/_)); |
| |
| EXPECT_START_CSR_TEMPORARY_UNAVAILABLE(ClientCertProvisioningStartCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| worker.DoStep(); |
| } |
| |
| Mock::VerifyAndClearExpectations(&cloud_policy_client_); |
| |
| { |
| EXPECT_START_CSR_TEMPORARY_UNAVAILABLE(ClientCertProvisioningStartCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| FastForwardBy(next_delay + small_delay * 10); |
| next_delay *= 2; |
| } |
| |
| Mock::VerifyAndClearExpectations(&cloud_policy_client_); |
| |
| { |
| EXPECT_START_CSR_TEMPORARY_UNAVAILABLE(ClientCertProvisioningStartCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| FastForwardBy(next_delay + small_delay * 10); |
| next_delay *= 2; |
| } |
| |
| Mock::VerifyAndClearExpectations(&cloud_policy_client_); |
| |
| { |
| EXPECT_START_CSR_TEMPORARY_UNAVAILABLE(ClientCertProvisioningStartCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| FastForwardBy(next_delay + small_delay); |
| next_delay *= 2; |
| } |
| } |
| |
| // Checks that the worker removes a key when an error occurs after the key was |
| // registered. |
| TEST_F(CertProvisioningWorkerTest, RemoveRegisteredKey) { |
| CertProfile cert_profile{kCertProfileId, kCertProfileVersion}; |
| MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey(); |
| MockCertProvisioningInvalidator* mock_invalidator = nullptr; |
| CertProvisioningWorkerImpl worker( |
| CertScope::kUser, GetProfile(), &testing_pref_service_, cert_profile, |
| &cloud_policy_client_, MakeInvalidator(&mock_invalidator), GetCallback()); |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_PREPARE_KEY_OK( |
| *mock_tpm_challenge_key, |
| StartPrepareKeyStep(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId), |
| /*profile=*/_, /*key_name_for_spkac=*/"", |
| /*callback=*/_)); |
| |
| EXPECT_START_CSR_OK(ClientCertProvisioningStartCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| |
| EXPECT_CALL(*mock_invalidator, Register(kInvalidationTopic, _)).Times(1); |
| |
| EXPECT_SIGN_CHALLENGE_OK( |
| *mock_tpm_challenge_key, |
| StartSignChallengeStep(kChallenge, /*include_signed_public_key=*/true, |
| /*callback=*/_)); |
| |
| EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep); |
| |
| EXPECT_SET_ATTRIBUTE_FOR_KEY_FAIL(SetAttributeForKey( |
| platform_keys::kTokenIdUser, kPublicKey, |
| platform_keys::KeyAttributeType::CertificateProvisioningId, |
| kCertProfileId, _)); |
| |
| EXPECT_CALL(*mock_invalidator, Unregister()).Times(1); |
| |
| EXPECT_CALL(*platform_keys_service_, |
| RemoveKey(platform_keys::kTokenIdUser, |
| /*public_key_spki_der=*/kPublicKey, /*callback=*/_)) |
| .Times(1) |
| .WillOnce(RunOnceCallback<2>(/*error_message=*/"")); |
| |
| EXPECT_CALL(callback_observer_, |
| Callback(cert_profile, CertProvisioningWorkerState::kFailed)) |
| .Times(1); |
| } |
| |
| worker.DoStep(); |
| FastForwardBy(TimeDelta::FromSeconds(1)); |
| } |
| |
| 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_->Get(pref_name_); |
| DCHECK(pref_value); |
| OnPrefValueUpdated(*pref_value); |
| } |
| |
| // Allows to add expectations about preference changes and verify new values. |
| MOCK_METHOD(void, OnPrefValueUpdated, (const base::Value& value)); |
| |
| private: |
| PrefService* service_ = nullptr; |
| const char* pref_name_ = nullptr; |
| PrefChangeRegistrar pref_change_registrar_; |
| base::WeakPtrFactory<PrefServiceObserver> weak_factory_{this}; |
| }; |
| |
| TEST_F(CertProvisioningWorkerTest, SerializationSuccess) { |
| CertProfile cert_profile{kCertProfileId, kCertProfileVersion}; |
| CertScope cert_scope = CertScope::kUser; |
| |
| 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( |
| cert_scope, GetProfile(), &testing_pref_service_, cert_profile, |
| &cloud_policy_client_, MakeInvalidator(), GetCallback()); |
| |
| StrictMock<PrefServiceObserver> pref_observer( |
| &testing_pref_service_, GetPrefNameForSerialization(CertScope::kUser)); |
| base::Value pref_val; |
| |
| // Prepare key, send start csr request. |
| { |
| testing::InSequence seq; |
| |
| EXPECT_PREPARE_KEY_OK( |
| *mock_tpm_challenge_key, |
| StartPrepareKeyStep(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId), |
| /*profile=*/_, /*key_name_for_spkac=*/"", |
| /*callback=*/_)); |
| |
| pref_val = ParseJson(R"({ |
| "cert_profile_1": { |
| "cert_profile": { |
| "policy_version": "cert_profile_version_1", |
| "profile_id": "cert_profile_1" |
| }, |
| "cert_scope": 0, |
| "invalidation_topic": "", |
| "public_key": "ZmFrZV9wdWJsaWNfa2V5XzE=", |
| "state": 1 |
| } |
| })"); |
| EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1); |
| |
| EXPECT_START_CSR_NO_OP(ClientCertProvisioningStartCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| |
| worker->DoStep(); |
| } |
| |
| // Recreate worker. |
| { |
| testing::InSequence seq; |
| |
| mock_tpm_challenge_key = PrepareTpmChallengeKey(); |
| |
| EXPECT_CALL( |
| *mock_tpm_challenge_key, |
| RestorePreparedKeyState(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId), _, "")) |
| .Times(1); |
| |
| worker = CertProvisioningWorkerFactory::Get()->Deserialize( |
| cert_scope, GetProfile(), &testing_pref_service_, |
| *pref_val.FindKeyOfType(kCertProfileId, base::Value::Type::DICTIONARY), |
| &cloud_policy_client_, MakeInvalidator(&mock_invalidator), |
| GetCallback()); |
| } |
| |
| // Retry start csr request, receive response, try sign challenge. |
| { |
| testing::InSequence seq; |
| |
| EXPECT_START_CSR_OK(ClientCertProvisioningStartCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| |
| pref_val = ParseJson("{}"); |
| EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1); |
| |
| EXPECT_CALL(*mock_invalidator, Register(kInvalidationTopic, _)).Times(1); |
| |
| EXPECT_SIGN_CHALLENGE_OK( |
| *mock_tpm_challenge_key, |
| StartSignChallengeStep(kChallenge, /*include_signed_public_key=*/true, |
| /*callback=*/_)); |
| |
| EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep); |
| |
| EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(SetAttributeForKey( |
| platform_keys::kTokenIdUser, kPublicKey, |
| platform_keys::KeyAttributeType::CertificateProvisioningId, |
| kCertProfileId, _)); |
| |
| EXPECT_SIGN_RSAPKC1_DIGEST_OK( |
| SignRSAPKCS1Digest(platform_keys::kTokenIdUser, kDataToSign, kPublicKey, |
| kPkHashAlgo, /*callback=*/_)); |
| |
| EXPECT_FINISH_CSR_OK(ClientCertProvisioningFinishCsr( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| kChallengeResponse, kSignature, /*callback=*/_)); |
| |
| pref_val = ParseJson(R"({ |
| "cert_profile_1": { |
| "cert_profile": { |
| "policy_version": "cert_profile_version_1", |
| "profile_id": "cert_profile_1" |
| }, |
| "cert_scope": 0, |
| "invalidation_topic": "fake_invalidation_topic_1", |
| "public_key": "ZmFrZV9wdWJsaWNfa2V5XzE=", |
| "state": 7 |
| } |
| })"); |
| EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1); |
| |
| EXPECT_DOWNLOAD_CERT_NO_OP(ClientCertProvisioningDownloadCert( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| |
| worker->DoStep(); |
| } |
| |
| // Recreate worker. |
| { |
| testing::InSequence seq; |
| |
| mock_invalidator_obj = MakeInvalidator(&mock_invalidator); |
| EXPECT_CALL(*mock_invalidator, Register(kInvalidationTopic, _)).Times(1); |
| |
| mock_tpm_challenge_key = PrepareTpmChallengeKey(); |
| EXPECT_CALL( |
| *mock_tpm_challenge_key, |
| RestorePreparedKeyState(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId), _, "")) |
| .Times(1); |
| |
| worker = CertProvisioningWorkerFactory::Get()->Deserialize( |
| cert_scope, GetProfile(), &testing_pref_service_, |
| *pref_val.FindKeyOfType(kCertProfileId, base::Value::Type::DICTIONARY), |
| &cloud_policy_client_, std::move(mock_invalidator_obj), GetCallback()); |
| } |
| |
| // Retry download cert request, receive response, try import certificate. |
| { |
| testing::InSequence seq; |
| |
| EXPECT_DOWNLOAD_CERT_OK(ClientCertProvisioningDownloadCert( |
| kCertScopeStrUser, kCertProfileId, kCertProfileVersion, kPublicKey, |
| /*callback=*/_)); |
| |
| EXPECT_IMPORT_CERTIFICATE_OK(ImportCertificate( |
| platform_keys::kTokenIdUser, /*certificate=*/_, /*callback=*/_)); |
| |
| pref_val = ParseJson("{}"); |
| EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1); |
| |
| EXPECT_CALL(*mock_invalidator, Unregister()).Times(1); |
| |
| EXPECT_CALL(callback_observer_, |
| Callback(cert_profile, CertProvisioningWorkerState::kSucceed)) |
| .Times(1); |
| worker->DoStep(); |
| } |
| } |
| |
| TEST_F(CertProvisioningWorkerTest, SerializationOnFailure) { |
| CertProfile cert_profile{kCertProfileId, kCertProfileVersion}; |
| |
| MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey(); |
| auto worker = CertProvisioningWorkerFactory::Get()->Create( |
| CertScope::kUser, GetProfile(), &testing_pref_service_, cert_profile, |
| &cloud_policy_client_, MakeInvalidator(), GetCallback()); |
| |
| PrefServiceObserver pref_observer( |
| &testing_pref_service_, GetPrefNameForSerialization(CertScope::kUser)); |
| base::Value pref_val; |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_PREPARE_KEY_OK( |
| *mock_tpm_challenge_key, |
| StartPrepareKeyStep(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId), |
| /*profile=*/_, /*key_name_for_spkac=*/"", |
| /*callback=*/_)); |
| |
| pref_val = ParseJson(R"({ |
| "cert_profile_1": { |
| "cert_profile": { |
| "policy_version": "cert_profile_version_1", |
| "profile_id": "cert_profile_1" |
| }, |
| "cert_scope": 0, |
| "invalidation_topic": "", |
| "public_key": "ZmFrZV9wdWJsaWNfa2V5XzE=", |
| "state": 1 |
| } |
| })"); |
| EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1); |
| |
| EXPECT_START_CSR_CA_ERROR(ClientCertProvisioningStartCsr); |
| |
| pref_val = ParseJson("{}"); |
| EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1); |
| |
| EXPECT_CALL( |
| fake_cryptohome_client_, |
| OnTpmAttestationDeleteKey(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId))) |
| .Times(1); |
| |
| EXPECT_CALL(callback_observer_, |
| Callback(cert_profile, CertProvisioningWorkerState::kFailed)) |
| .Times(1); |
| } |
| |
| worker->DoStep(); |
| FastForwardBy(TimeDelta::FromSeconds(1)); |
| } |
| |
| TEST_F(CertProvisioningWorkerTest, InformationalGetters) { |
| CertProfile cert_profile{kCertProfileId, kCertProfileVersion}; |
| |
| MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey(); |
| CertProvisioningWorkerImpl worker( |
| CertScope::kUser, GetProfile(), &testing_pref_service_, cert_profile, |
| &cloud_policy_client_, MakeInvalidator(), GetCallback()); |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_PREPARE_KEY_OK(*mock_tpm_challenge_key, StartPrepareKeyStep); |
| |
| EXPECT_START_CSR_TRY_LATER(ClientCertProvisioningStartCsr, |
| TimeDelta::FromSeconds(30).InMilliseconds()); |
| |
| worker.DoStep(); |
| EXPECT_EQ(worker.GetState(), |
| CertProvisioningWorkerState::kKeypairGenerated); |
| EXPECT_EQ(worker.GetPreviousState(), |
| CertProvisioningWorkerState::kInitState); |
| EXPECT_EQ(worker.GetCertProfile(), cert_profile); |
| EXPECT_EQ(worker.GetPublicKey(), kPublicKey); |
| } |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_START_CSR_CA_ERROR(ClientCertProvisioningStartCsr); |
| |
| EXPECT_CALL( |
| fake_cryptohome_client_, |
| OnTpmAttestationDeleteKey(attestation::AttestationKeyType::KEY_USER, |
| GetKeyName(kCertProfileId))) |
| .Times(1); |
| |
| EXPECT_CALL(callback_observer_, |
| Callback(cert_profile, CertProvisioningWorkerState::kFailed)) |
| .Times(1); |
| |
| worker.DoStep(); |
| FastForwardBy(TimeDelta::FromSeconds(1)); |
| |
| EXPECT_EQ(worker.GetState(), CertProvisioningWorkerState::kFailed); |
| EXPECT_EQ(worker.GetPreviousState(), |
| CertProvisioningWorkerState::kKeypairGenerated); |
| EXPECT_EQ(worker.GetCertProfile(), cert_profile); |
| EXPECT_EQ(worker.GetPublicKey(), kPublicKey); |
| } |
| } |
| |
| TEST_F(CertProvisioningWorkerTest, CancelDeviceWorker) { |
| CertScope cert_scope = CertScope::kDevice; |
| CertProfile cert_profile{kCertProfileId, kCertProfileVersion}; |
| |
| MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey(); |
| auto worker = CertProvisioningWorkerFactory::Get()->Create( |
| cert_scope, GetProfile(), &testing_pref_service_, cert_profile, |
| &cloud_policy_client_, MakeInvalidator(), GetCallback()); |
| |
| EXPECT_CALL(callback_observer_, Callback).Times(0); |
| |
| PrefServiceObserver pref_observer( |
| &testing_pref_service_, GetPrefNameForSerialization(CertScope::kDevice)); |
| base::Value pref_val; |
| |
| { |
| testing::InSequence seq; |
| |
| EXPECT_PREPARE_KEY_OK( |
| *mock_tpm_challenge_key, |
| StartPrepareKeyStep(attestation::AttestationKeyType::KEY_DEVICE, |
| /*key_name=*/"", |
| /*profile=*/_, |
| /*key_name_for_spkac=*/GetKeyName(kCertProfileId), |
| /*callback=*/_)); |
| |
| pref_val = ParseJson(R"({ |
| "cert_profile_1": { |
| "cert_profile": { |
| "policy_version": "cert_profile_version_1", |
| "profile_id": "cert_profile_1" |
| }, |
| "cert_scope": 1, |
| "invalidation_topic": "", |
| "public_key": "ZmFrZV9wdWJsaWNfa2V5XzE=", |
| "state": 1 |
| } |
| })"); |
| EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1); |
| |
| EXPECT_START_CSR_NO_OP(ClientCertProvisioningStartCsr); |
| |
| worker->DoStep(); |
| } |
| |
| { |
| EXPECT_CALL( |
| fake_cryptohome_client_, |
| OnTpmAttestationDeleteKey(attestation::AttestationKeyType::KEY_DEVICE, |
| GetKeyName(kCertProfileId))) |
| .Times(1); |
| |
| pref_val = ParseJson("{}"); |
| EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1); |
| |
| worker->Cancel(); |
| } |
| } |
| |
| } // namespace |
| } // namespace cert_provisioning |
| } // namespace chromeos |