| // 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 <algorithm> |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include "ash/constants/ash_switches.h" |
| #include "base/base64.h" |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/command_line.h" |
| #include "base/run_loop.h" |
| #include "chrome/browser/ash/arc/enterprise/cert_store/cert_store_service.h" |
| #include "chrome/browser/ash/arc/keymaster/arc_keymaster_bridge.h" |
| #include "chrome/browser/ash/arc/session/arc_service_launcher.h" |
| #include "chrome/browser/ash/policy/affiliation/affiliation_mixin.h" |
| #include "chrome/browser/ash/policy/affiliation/affiliation_test_helper.h" |
| #include "chrome/browser/ash/profiles/profile_helper.h" |
| #include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_service_factory.h" |
| #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h" |
| #include "chrome/browser/net/nss_context.h" |
| #include "chrome/browser/platform_keys/extension_key_permissions_service.h" |
| #include "chrome/browser/platform_keys/extension_key_permissions_service_factory.h" |
| #include "chrome/browser/platform_keys/platform_keys.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/common/net/x509_certificate_model_nss.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/services/keymaster/public/mojom/cert_store.mojom.h" |
| #include "chrome/test/base/mixin_based_in_process_browser_test.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chromeos/network/network_cert_loader.h" |
| #include "components/arc/arc_prefs.h" |
| #include "components/arc/session/arc_bridge_service.h" |
| #include "components/arc/test/arc_util_test_support.h" |
| #include "components/policy/policy_constants.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/test_launcher.h" |
| #include "crypto/scoped_test_system_nss_key_slot.h" |
| #include "extensions/browser/extension_system.h" |
| #include "net/cert/nss_cert_database.h" |
| #include "net/cert/x509_util_nss.h" |
| #include "net/test/cert_test_util.h" |
| #include "net/test/test_data_directory.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace arc { |
| |
| namespace { |
| |
| constexpr char kFileName1[] = "client_1"; |
| constexpr char kFileName2[] = "client_2"; |
| constexpr char kFileName3[] = "client_3"; |
| constexpr char kFileName4[] = "client_4"; |
| |
| constexpr char kFakeExtensionId[] = "fakeextensionid"; |
| |
| // Contains information needed for test cert parameters. |
| struct TestCertData { |
| TestCertData(const std::string& file_name, |
| bool is_corporate_usage, |
| keymaster::mojom::ChapsSlot slot) |
| : file_name(file_name), |
| is_corporate_usage(is_corporate_usage), |
| slot(slot) { |
| // Keys in system slot must be corporate usage. |
| DCHECK(slot != keymaster::mojom::ChapsSlot::kSystem || is_corporate_usage); |
| } |
| TestCertData(const TestCertData&) = default; |
| bool operator==(const TestCertData& other) const { |
| return std::tie(file_name, is_corporate_usage, slot) == |
| std::tie(other.file_name, other.is_corporate_usage, other.slot); |
| } |
| |
| std::string file_name; |
| bool is_corporate_usage; |
| keymaster::mojom::ChapsSlot slot; |
| }; |
| |
| // Associates a |test_data| to its |nss_cert| once installed. |
| struct InstalledTestCert { |
| InstalledTestCert(TestCertData test_data, net::ScopedCERTCertificate nss_cert) |
| : test_data(test_data), nss_cert(std::move(nss_cert)) {} |
| TestCertData test_data; |
| net::ScopedCERTCertificate nss_cert; |
| }; |
| |
| std::string GetDerCert64(CERTCertificate* cert) { |
| std::string der_cert; |
| EXPECT_TRUE(net::x509_util::GetDEREncoded(cert, &der_cert)); |
| std::string der_cert64; |
| base::Base64Encode(der_cert, &der_cert64); |
| return der_cert64; |
| } |
| |
| class FakeArcCertInstaller : public ArcCertInstaller { |
| public: |
| FakeArcCertInstaller(Profile* profile, |
| std::unique_ptr<policy::RemoteCommandsQueue> queue) |
| : ArcCertInstaller(profile, std::move(queue)) {} |
| |
| // Returns map from nicknames to real der cert64 to identify certificates. |
| std::map<std::string, std::string> InstallArcCerts( |
| std::vector<CertDescription> certs, |
| InstallArcCertsCallback callback) override { |
| certs_.clear(); |
| for (const auto& cert : certs) { |
| certs_[x509_certificate_model::GetCertNameOrNickname( |
| cert.nss_cert.get())] = GetDerCert64(cert.nss_cert.get()); |
| } |
| |
| callback_ = std::move(callback); |
| Stop(); |
| return certs_; |
| } |
| |
| void RunCompletionCallback(bool success) { |
| std::move(callback_).Run(success); |
| } |
| |
| void Wait() { |
| run_loop_ = std::make_unique<base::RunLoop>(); |
| run_loop_->Run(); |
| } |
| |
| void Stop() { |
| if (run_loop_) |
| run_loop_->QuitWhenIdle(); |
| } |
| |
| std::map<std::string, std::string> certs() const { return certs_; } |
| |
| private: |
| std::unique_ptr<base::RunLoop> run_loop_; |
| std::map<std::string, std::string> certs_; |
| InstallArcCertsCallback callback_; |
| }; |
| |
| class FakeArcKeymasterBridge : public ArcKeymasterBridge { |
| public: |
| explicit FakeArcKeymasterBridge(content::BrowserContext* context) |
| : ArcKeymasterBridge(context, nullptr) {} |
| FakeArcKeymasterBridge(const FakeArcKeymasterBridge& other) = delete; |
| FakeArcKeymasterBridge& operator=(const FakeArcKeymasterBridge&) = delete; |
| |
| void UpdatePlaceholderKeys(std::vector<keymaster::mojom::ChromeOsKeyPtr> keys, |
| UpdatePlaceholderKeysCallback callback) override { |
| keys_ = std::move(keys); |
| std::move(callback).Run(/*success=*/true); |
| } |
| |
| const std::vector<keymaster::mojom::ChromeOsKeyPtr>& placeholder_keys() |
| const { |
| return keys_; |
| } |
| |
| private: |
| std::vector<keymaster::mojom::ChromeOsKeyPtr> keys_; |
| }; |
| |
| std::unique_ptr<KeyedService> BuildFakeArcKeymasterBridge( |
| content::BrowserContext* profile) { |
| return std::make_unique<FakeArcKeymasterBridge>(profile); |
| } |
| |
| std::unique_ptr<KeyedService> BuildCertStoreService( |
| std::unique_ptr<FakeArcCertInstaller> installer, |
| content::BrowserContext* profile) { |
| return std::make_unique<CertStoreService>(profile, std::move(installer)); |
| } |
| |
| // The following series of functions related to IsSystemSlotAvailable use the |
| // NSSCertDatabase. The cert database is accessed through a raw pointer with |
| // limited lifetime guarantees and is not thread safe. Namely, the cert database |
| // is guaranteed valid for the single IO thread task where it was received. |
| // |
| // Furthermore, creating an NssCertDatabaseGetter requires a BrowserContext, |
| // which can only be accessed on the UI thread. |
| // |
| // ListCerts and related functions are implemented to make sure the above |
| // requirements are respected. Here's a diagram of the interaction between UI |
| // and IO threads. |
| // |
| // UI Thread IO Thread |
| // |
| // IsSystemSlotAvailable |
| // | |
| // run_loop.QuitClosure |
| // | |
| // CreateNSSCertDatabaseGetter |
| // | |
| // \--------------------------------v |
| // IsSystemSlotAvailableWithDbGetterOnIO |
| // | |
| // database_getter.Run |
| // | |
| // IsSystemSlotAvailableOnIO |
| // | |
| // GetSystemSlot |
| // | |
| // quit_closure.Run |
| |
| void IsSystemSlotAvailableOnIO(bool* out_system_slot_available, |
| base::OnceClosure done_closure, |
| net::NSSCertDatabase* cert_db) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| *out_system_slot_available = cert_db->GetSystemSlot() != nullptr; |
| std::move(done_closure).Run(); |
| } |
| |
| void IsSystemSlotAvailableWithDbGetterOnIO( |
| NssCertDatabaseGetter database_getter, |
| bool* out_system_slot_available, |
| base::OnceClosure done_closure) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| auto did_get_cert_db_split_callback = base::SplitOnceCallback( |
| base::BindOnce(&IsSystemSlotAvailableOnIO, out_system_slot_available, |
| std::move(done_closure))); |
| net::NSSCertDatabase* cert_db = |
| std::move(database_getter) |
| .Run(std::move(did_get_cert_db_split_callback.first)); |
| if (cert_db) { |
| std::move(did_get_cert_db_split_callback.second).Run(cert_db); |
| } |
| } |
| |
| // Returns trus if the test system slot was setup correctly and is available. |
| bool IsSystemSlotAvailable(Profile* profile) { |
| // |profile| must be accessed on the UI thread. |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| base::RunLoop run_loop; |
| bool system_slot_available = false; |
| // The NssCertDatabaseGetter must be posted to the IO thread immediately. |
| content::GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(IsSystemSlotAvailableWithDbGetterOnIO, |
| CreateNSSCertDatabaseGetter(profile), |
| &system_slot_available, run_loop.QuitClosure())); |
| run_loop.Run(); |
| return system_slot_available; |
| } |
| |
| // Returns the number of corporate usage certs in |test_certs|. |
| size_t CountCorporateUsage(const std::vector<TestCertData>& test_certs) { |
| return std::count_if(test_certs.begin(), test_certs.end(), |
| [](const TestCertData& test_data) { |
| return test_data.is_corporate_usage; |
| }); |
| } |
| |
| // Deletes the given |cert| from |cert_db|. |
| void DeleteCertAndKey(CERTCertificate* cert, |
| base::OnceClosure done_callback, |
| net::NSSCertDatabase* cert_db) { |
| base::ScopedAllowBlockingForTesting allow_io; |
| EXPECT_TRUE(cert_db->DeleteCertAndKey(cert)); |
| std::move(done_callback).Run(); |
| } |
| |
| // Called once a key has been registered as corporate usage. |
| void OnKeyRegisteredForCorporateUsage(base::OnceClosure done_callback, |
| bool is_error, |
| crosapi::mojom::KeystoreError error) { |
| ASSERT_FALSE(is_error) << static_cast<int>(error); |
| std::move(done_callback).Run(); |
| } |
| |
| // Uses |service| to register |cert| as corporate usage. |
| void RegisterCorporateKeyWithService( |
| CERTCertificate* cert, |
| base::OnceClosure done_callback, |
| std::unique_ptr<chromeos::platform_keys::ExtensionKeyPermissionsService> |
| service) { |
| std::string client_cert_spki( |
| cert->derPublicKey.data, |
| cert->derPublicKey.data + cert->derPublicKey.len); |
| service->RegisterKeyForCorporateUsage( |
| client_cert_spki, base::BindOnce(&OnKeyRegisteredForCorporateUsage, |
| std::move(done_callback))); |
| } |
| |
| } // namespace |
| |
| class CertStoreServiceTest |
| : public MixinBasedInProcessBrowserTest, |
| public ::testing::WithParamInterface<std::vector<TestCertData>> { |
| public: |
| CertStoreServiceTest() = default; |
| CertStoreServiceTest(const CertStoreServiceTest& other) = delete; |
| CertStoreServiceTest& operator=(const CertStoreServiceTest&) = delete; |
| |
| protected: |
| void SetUpCommandLine(base::CommandLine* command_line) override; |
| |
| void SetUpInProcessBrowserTestFixture() override; |
| |
| void SetUpOnMainThread() override; |
| |
| void TearDownOnMainThread() override; |
| |
| void TearDownInProcessBrowserTestFixture() override; |
| |
| // Installs the given |certs_to_setup| in the NSS database. |
| void SetUpCerts(const std::vector<TestCertData>& certs_to_setup); |
| |
| // Registers the given |cert| as corporate usage through platform keys. |
| void RegisterCorporateKey(CERTCertificate* cert); |
| |
| // Deletes the given |cert| from the NSS cert database. |
| void DeleteCert(CERTCertificate* cert); |
| |
| // Verifies the expected |test_certs| are installed correctly. |
| void CheckInstalledCerts(std::vector<TestCertData> test_certs, |
| CertStoreService* service); |
| |
| // Returns the profile for the |affiliation_mixin_| account. |
| Profile* profile(); |
| |
| // Owned by the CertStoreService instance. |
| FakeArcCertInstaller* installer_; |
| |
| std::vector<InstalledTestCert> installed_certs_; |
| |
| chromeos::DeviceStateMixin device_state_{ |
| &mixin_host_, |
| chromeos::DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED}; |
| policy::DevicePolicyCrosTestHelper device_policy_helper_; |
| policy::AffiliationMixin affiliation_mixin_{&mixin_host_, |
| &device_policy_helper_}; |
| |
| private: |
| // Creates ScopedCERTCertificates for each |certs_to_setup| and appends them |
| // to |installed_certs_|. |
| void SetUpTestClientCerts(const std::vector<TestCertData>& certs_to_setup, |
| base::OnceClosure done_callback, |
| net::NSSCertDatabase* cert_db); |
| |
| // Imports the list of |installed_certs_| into the NSS |cert_db|. |
| void ImportTestClientCerts(base::OnceClosure done_callback, |
| net::NSSCertDatabase* cert_db); |
| |
| // Checks that |keymaster_bridge_->placeholder_keys()| contains a key with |
| // given |id| and |slot|. |
| bool PlaceholdersContainIdAndSlot(const std::string& id, |
| keymaster::mojom::ChapsSlot slot); |
| |
| // Initializes |test_system_slot_|. |
| void SetUpTestSystemSlot(); |
| |
| void SetUpTestSystemSlotOnIO(bool* out_system_slot_constructed_successfully); |
| |
| // Destroys |test_system_slot_|. |
| void TearDownTestSystemSlot(); |
| |
| void TearDownTestSystemSlotOnIO(); |
| |
| std::unique_ptr<crypto::ScopedTestSystemNSSKeySlot> test_system_slot_; |
| |
| // Owned by the CertStoreService instance. |
| FakeArcKeymasterBridge* keymaster_bridge_; |
| }; |
| |
| void CertStoreServiceTest::SetUpCommandLine(base::CommandLine* command_line) { |
| MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line); |
| arc::SetArcAvailableCommandLineForTesting(command_line); |
| policy::AffiliationTestHelper::AppendCommandLineSwitchesForLoginManager( |
| command_line); |
| } |
| |
| void CertStoreServiceTest::SetUpInProcessBrowserTestFixture() { |
| MixinBasedInProcessBrowserTest::SetUpInProcessBrowserTestFixture(); |
| chromeos::platform_keys::PlatformKeysServiceFactory::GetInstance() |
| ->SetTestingMode(true); |
| } |
| |
| void CertStoreServiceTest::SetUpOnMainThread() { |
| MixinBasedInProcessBrowserTest::SetUpOnMainThread(); |
| |
| // Pre tests need no further setup. |
| if (content::IsPreTest()) |
| return; |
| |
| policy::AffiliationTestHelper::LoginUser(affiliation_mixin_.account_id()); |
| |
| // Use fake ArcKeymasterBridge. |
| ArcKeymasterBridge::GetFactory()->SetTestingFactoryAndUse( |
| profile(), base::BindRepeating(&BuildFakeArcKeymasterBridge)); |
| auto* keymaster_bridge = ArcKeymasterBridge::GetForBrowserContext(profile()); |
| keymaster_bridge_ = static_cast<FakeArcKeymasterBridge*>(keymaster_bridge); |
| |
| // Use fake ArcCertInstaller in CertStoreService. |
| auto installer = std::make_unique<FakeArcCertInstaller>( |
| profile(), std::make_unique<policy::RemoteCommandsQueue>()); |
| installer_ = installer.get(); |
| CertStoreService::GetFactory()->SetTestingFactoryAndUse( |
| profile(), base::BindRepeating(&BuildCertStoreService, |
| base::Passed(std::move(installer)))); |
| |
| // Set up a system slot so tests can access device certs. |
| ASSERT_NO_FATAL_FAILURE(SetUpTestSystemSlot()); |
| ASSERT_TRUE(IsSystemSlotAvailable(profile())); |
| } |
| |
| void CertStoreServiceTest::TearDownOnMainThread() { |
| TearDownTestSystemSlot(); |
| MixinBasedInProcessBrowserTest::TearDownOnMainThread(); |
| } |
| |
| void CertStoreServiceTest::TearDownInProcessBrowserTestFixture() { |
| chromeos::platform_keys::PlatformKeysServiceFactory::GetInstance() |
| ->SetTestingMode(false); |
| MixinBasedInProcessBrowserTest::TearDownInProcessBrowserTestFixture(); |
| } |
| |
| void CertStoreServiceTest::SetUpCerts( |
| const std::vector<TestCertData>& certs_to_setup) { |
| // Remember current size of |installed_certs_| before new certs. |
| size_t initial_size = installed_certs_.size(); |
| |
| // Read certs from files. |
| base::RunLoop loop; |
| GetNSSCertDatabaseForProfile( |
| profile(), base::BindOnce(&CertStoreServiceTest::SetUpTestClientCerts, |
| base::Unretained(this), certs_to_setup, |
| loop.QuitClosure())); |
| loop.Run(); |
| |
| // Verify |certs_to_setup.size()| new certs have been installed. |
| ASSERT_EQ(installed_certs_.size(), certs_to_setup.size() + initial_size); |
| |
| // Register newly installed certs for corporate usage if needed. |
| for (size_t i = initial_size; i < installed_certs_.size(); ++i) { |
| const InstalledTestCert& cert = installed_certs_[i]; |
| if (cert.test_data.is_corporate_usage) |
| RegisterCorporateKey(cert.nss_cert.get()); |
| } |
| |
| // Import certs into database. |
| { |
| base::RunLoop loop; |
| GetNSSCertDatabaseForProfile( |
| profile(), base::BindOnce(&CertStoreServiceTest::ImportTestClientCerts, |
| base::Unretained(this), loop.QuitClosure())); |
| loop.Run(); |
| } |
| } |
| |
| void CertStoreServiceTest::RegisterCorporateKey(CERTCertificate* cert) { |
| base::RunLoop run_loop; |
| chromeos::platform_keys::ExtensionKeyPermissionsServiceFactory:: |
| GetForBrowserContextAndExtension( |
| base::BindOnce(&RegisterCorporateKeyWithService, cert, |
| run_loop.QuitClosure()), |
| profile(), kFakeExtensionId); |
| run_loop.Run(); |
| } |
| |
| void CertStoreServiceTest::DeleteCert(CERTCertificate* cert) { |
| base::RunLoop loop; |
| GetNSSCertDatabaseForProfile( |
| profile(), base::BindOnce(&DeleteCertAndKey, cert, loop.QuitClosure())); |
| loop.Run(); |
| installed_certs_.pop_back(); |
| } |
| |
| void CertStoreServiceTest::CheckInstalledCerts( |
| std::vector<TestCertData> test_certs, |
| CertStoreService* service) { |
| // Verify the number of corporate usage certs reported is correct. |
| EXPECT_EQ(CountCorporateUsage(test_certs), installer_->certs().size()); |
| EXPECT_EQ(CountCorporateUsage(test_certs), |
| service->get_required_cert_names().size()); |
| EXPECT_EQ(CountCorporateUsage(test_certs), |
| keymaster_bridge_->placeholder_keys().size()); |
| |
| // Verify |test_certs| and |installed_certs_| have matching elements. |
| ASSERT_EQ(test_certs.size(), installed_certs_.size()); |
| for (size_t i = 0; i < installed_certs_.size(); ++i) |
| EXPECT_EQ(test_certs[i], installed_certs_[i].test_data); |
| |
| for (const auto& cert_name : service->get_required_cert_names()) { |
| bool found = false; |
| // Check the required cert is installed. |
| ASSERT_TRUE(installer_->certs().count(cert_name)); |
| for (const auto& cert : installed_certs_) { |
| // Check the required cert is one of the installed test certificates. |
| const net::ScopedCERTCertificate& nss_cert = cert.nss_cert; |
| |
| // Skip until |cert| corresponds to the current |cert_name|. |
| if (GetDerCert64(nss_cert.get()) != installer_->certs()[cert_name]) |
| continue; |
| |
| // Check nickname. |
| EXPECT_EQ(x509_certificate_model::GetCertNameOrNickname(nss_cert.get()), |
| cert_name); |
| found = true; |
| // Check KeyInfo. |
| auto key_info = |
| service->GetKeyInfoForDummySpki(installer_->certs()[cert_name]); |
| EXPECT_TRUE(key_info.has_value()); |
| EXPECT_EQ(key_info.value().nickname, cert_name); |
| // Check CKA_ID and slot. |
| int slot_id; |
| std::string hex_encoded_id = base::HexEncode(key_info.value().id.data(), |
| key_info.value().id.size()); |
| EXPECT_EQ(hex_encoded_id, |
| chromeos::NetworkCertLoader::GetPkcs11IdAndSlotForCert( |
| nss_cert.get(), &slot_id)); |
| EXPECT_TRUE(PlaceholdersContainIdAndSlot(key_info.value().id, |
| cert.test_data.slot)); |
| break; |
| } |
| // Check the required cert was found. |
| EXPECT_TRUE(found); |
| } |
| } |
| |
| Profile* CertStoreServiceTest::profile() { |
| return chromeos::ProfileHelper::Get()->GetProfileByAccountId( |
| affiliation_mixin_.account_id()); |
| } |
| |
| void CertStoreServiceTest::SetUpTestClientCerts( |
| const std::vector<TestCertData>& certs_to_setup, |
| base::OnceClosure done_callback, |
| net::NSSCertDatabase* cert_db) { |
| for (const auto& test_data : certs_to_setup) { |
| base::ScopedAllowBlockingForTesting allow_io; |
| net::ImportSensitiveKeyFromFile( |
| net::GetTestCertsDirectory(), test_data.file_name + ".pk8", |
| test_data.slot == keymaster::mojom::ChapsSlot::kUser |
| ? cert_db->GetPrivateSlot().get() |
| : cert_db->GetSystemSlot().get()); |
| net::ScopedCERTCertificateList certs = |
| net::CreateCERTCertificateListFromFile( |
| net::GetTestCertsDirectory(), test_data.file_name + ".pem", |
| net::X509Certificate::FORMAT_AUTO); |
| ASSERT_EQ(1U, certs.size()); |
| |
| installed_certs_.emplace_back( |
| test_data, net::x509_util::DupCERTCertificate(certs[0].get())); |
| } |
| std::move(done_callback).Run(); |
| } |
| |
| void CertStoreServiceTest::ImportTestClientCerts( |
| base::OnceClosure done_callback, |
| net::NSSCertDatabase* cert_db) { |
| for (const auto& cert : installed_certs_) { |
| // Import user certificate properly how it's done in PlatformKeys. |
| cert_db->ImportUserCert(cert.nss_cert.get()); |
| } |
| std::move(done_callback).Run(); |
| } |
| |
| bool CertStoreServiceTest::PlaceholdersContainIdAndSlot( |
| const std::string& id, |
| keymaster::mojom::ChapsSlot slot) { |
| for (const auto& key : keymaster_bridge_->placeholder_keys()) { |
| if (key->key_data->is_chaps_key_data() && |
| key->key_data->get_chaps_key_data()->id == id && |
| key->key_data->get_chaps_key_data()->slot == slot) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void CertStoreServiceTest::SetUpTestSystemSlot() { |
| bool system_slot_constructed_successfully = false; |
| base::RunLoop loop; |
| content::GetIOThreadTaskRunner({})->PostTaskAndReply( |
| FROM_HERE, |
| base::BindOnce(&CertStoreServiceTest::SetUpTestSystemSlotOnIO, |
| base::Unretained(this), |
| &system_slot_constructed_successfully), |
| loop.QuitClosure()); |
| loop.Run(); |
| ASSERT_TRUE(system_slot_constructed_successfully); |
| } |
| |
| void CertStoreServiceTest::SetUpTestSystemSlotOnIO( |
| bool* out_system_slot_constructed_successfully) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| test_system_slot_ = std::make_unique<crypto::ScopedTestSystemNSSKeySlot>(); |
| *out_system_slot_constructed_successfully = |
| test_system_slot_->ConstructedSuccessfully(); |
| } |
| |
| void CertStoreServiceTest::TearDownTestSystemSlot() { |
| if (!test_system_slot_) |
| return; |
| |
| base::RunLoop loop; |
| content::GetIOThreadTaskRunner({})->PostTaskAndReply( |
| FROM_HERE, |
| base::BindOnce(&CertStoreServiceTest::TearDownTestSystemSlotOnIO, |
| base::Unretained(this)), |
| loop.QuitClosure()); |
| loop.Run(); |
| } |
| |
| void CertStoreServiceTest::TearDownTestSystemSlotOnIO() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| test_system_slot_.reset(); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CertStoreServiceTest, PRE_HandlesCorporateUsageCerts) { |
| policy::AffiliationTestHelper::PreLoginUser(affiliation_mixin_.account_id()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CertStoreServiceTest, HandlesCorporateUsageCerts) { |
| CertStoreService* service = CertStoreService::GetForBrowserContext(profile()); |
| ASSERT_TRUE(service); |
| |
| // Install all certs from parameter at once. |
| ASSERT_NO_FATAL_FAILURE(SetUpCerts(GetParam())); |
| installer_->Wait(); |
| installer_->RunCompletionCallback(true /* success */); |
| |
| // Verify all certs are installed correctly. |
| ASSERT_NO_FATAL_FAILURE(CheckInstalledCerts(GetParam(), service)); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CertStoreServiceTest, |
| PRE_InstallsAndDeletesCorporateUsageCerts) { |
| policy::AffiliationTestHelper::PreLoginUser(affiliation_mixin_.account_id()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(CertStoreServiceTest, |
| InstallsAndDeletesCorporateUsageCerts) { |
| CertStoreService* service = CertStoreService::GetForBrowserContext(profile()); |
| ASSERT_TRUE(service); |
| |
| // Install certs from parameter one by one. |
| for (size_t i = 0; i < GetParam().size(); ++i) { |
| ASSERT_NO_FATAL_FAILURE(SetUpCerts({GetParam()[i]})); |
| installer_->Wait(); |
| installer_->RunCompletionCallback(true /* success */); |
| |
| // Verify only the first (i+1) certs are installed so far. |
| ASSERT_NO_FATAL_FAILURE( |
| CheckInstalledCerts(std::vector<TestCertData>( |
| GetParam().begin(), GetParam().begin() + i + 1), |
| service)); |
| } |
| |
| // Uninstall certs from parameter one by one, from last to first. |
| for (size_t i = GetParam().size(); i--;) { |
| DeleteCert(installed_certs_.back().nss_cert.get()); |
| installer_->Wait(); |
| installer_->RunCompletionCallback(true /* success */); |
| |
| // Verify only the first i certs are left after the uninstall. |
| ASSERT_NO_FATAL_FAILURE(CheckInstalledCerts( |
| std::vector<TestCertData>(GetParam().begin(), GetParam().begin() + i), |
| service)); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| CertStoreTests, |
| CertStoreServiceTest, |
| ::testing::Values( |
| // No corporate usage keys. |
| std::vector<TestCertData>{ |
| TestCertData(kFileName1, |
| false /* is_corporate_usage */, |
| keymaster::mojom::ChapsSlot::kUser), |
| TestCertData(kFileName2, |
| false /* is_corporate_usage */, |
| keymaster::mojom::ChapsSlot::kUser)}, |
| // Corporate usage keys in user slot. |
| std::vector<TestCertData>{ |
| TestCertData(kFileName1, |
| true /* is_corporate_usage */, |
| keymaster::mojom::ChapsSlot::kUser), |
| TestCertData(kFileName2, |
| true /* is_corporate_usage */, |
| keymaster::mojom::ChapsSlot::kUser)}, |
| // Corporate usage keys in system slot. |
| std::vector<TestCertData>{ |
| TestCertData(kFileName1, |
| true /* is_corporate_usage */, |
| keymaster::mojom::ChapsSlot::kSystem), |
| TestCertData(kFileName2, |
| true /* is_corporate_usage */, |
| keymaster::mojom::ChapsSlot::kSystem)}, |
| // Corporate usage keys in both slots. |
| std::vector<TestCertData>{ |
| TestCertData(kFileName1, |
| true /* is_corporate_usage */, |
| keymaster::mojom::ChapsSlot::kUser), |
| TestCertData(kFileName2, |
| true /* is_corporate_usage */, |
| keymaster::mojom::ChapsSlot::kSystem), |
| TestCertData(kFileName3, |
| false /* is_corporate_usage */, |
| keymaster::mojom::ChapsSlot::kUser), |
| TestCertData(kFileName4, |
| true /* is_corporate_usage */, |
| keymaster::mojom::ChapsSlot::kSystem)})); |
| |
| } // namespace arc |