| // Copyright 2022 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/ownership/owner_key_loader.h" |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "ash/constants/ash_features.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/test_future.h" |
| #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" |
| #include "chrome/browser/ash/ownership/ownership_histograms.h" |
| #include "chrome/browser/ash/policy/core/device_policy_builder.h" |
| #include "chrome/browser/ash/settings/device_settings_service.h" |
| #include "chrome/browser/net/fake_nss_service.h" |
| #include "chrome/test/base/scoped_testing_local_state.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/ownership/mock_owner_key_util.h" |
| #include "components/ownership/owner_key_util_impl.h" |
| #include "components/prefs/testing_pref_service.h" |
| #include "components/user_manager/scoped_user_manager.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "crypto/rsa_private_key.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using PublicKeyRefPtr = scoped_refptr<ownership::PublicKey>; |
| using PrivateKeyRefPtr = scoped_refptr<ownership::PrivateKey>; |
| using base::Bucket; |
| |
| namespace ash { |
| |
| constexpr char kUserEmail[] = "user@example.com"; |
| |
| std::vector<uint8_t> ExtractSpkiDer( |
| const std::unique_ptr<crypto::RSAPrivateKey>& key) { |
| std::vector<uint8_t> bytes; |
| key->ExportPublicKey(&bytes); |
| return bytes; |
| } |
| |
| std::vector<uint8_t> ExtractSpkiDer(const crypto::ScopedSECKEYPrivateKey& key) { |
| crypto::ScopedSECKEYPublicKey public_key( |
| SECKEY_ConvertToPublicKey(key.get())); |
| |
| SECItem* public_key_bytes = PK11_DEREncodePublicKey(public_key.get()); |
| std::vector<uint8_t> bytes(public_key_bytes->data, |
| public_key_bytes->data + public_key_bytes->len); |
| SECITEM_FreeItem(public_key_bytes, PR_TRUE); |
| |
| return bytes; |
| } |
| |
| class OwnerKeyLoaderTestBase : public testing::Test { |
| public: |
| explicit OwnerKeyLoaderTestBase(user_manager::UserType user_type) |
| : user_type_(user_type) {} |
| |
| // testing::Test: |
| void SetUp() override { |
| auto fake_user_manager = std::make_unique<ash::FakeChromeUserManager>(); |
| user_manager_ = fake_user_manager.get(); |
| scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>( |
| std::move(fake_user_manager)); |
| |
| owner_key_util_ = base::MakeRefCounted<ownership::MockOwnerKeyUtil>(); |
| |
| device_settings_service_.SetSessionManager(&session_manager_client_, |
| owner_key_util_); |
| |
| profile_ = TestingProfile::Builder().SetProfileName(kUserEmail).Build(); |
| |
| user_manager_->AddUserWithAffiliationAndTypeAndProfile( |
| AccountId::FromUserEmail(kUserEmail), /*is_affiliated=*/false, |
| user_type_, profile_.get()); |
| |
| nss_service_ = FakeNssService::InitializeForBrowserContext( |
| profile_.get(), |
| /*enable_system_slot=*/false); |
| |
| key_loader_ = std::make_unique<OwnerKeyLoader>( |
| profile_.get(), &device_settings_service_, owner_key_util_, |
| /*is_enterprise_enrolled=*/false, result_observer_.GetCallback()); |
| } |
| |
| protected: |
| std::unique_ptr<crypto::RSAPrivateKey> ConfigureExistingPolicies( |
| const std::string& owner_username) { |
| // The actual content of the policies doesn't matter, OwnerKeyLoader only |
| // looks at the username that created them (i.e. at the user that was |
| // recognised as the owner when they were created). |
| policy::DevicePolicyBuilder policy_builder; |
| policy_builder.policy_data().set_username(owner_username); |
| policy_builder.Build(); |
| session_manager_client_.set_device_policy(policy_builder.GetBlob()); |
| return policy_builder.GetSigningKey(); |
| } |
| |
| // Checks whether the private key for `public_key_spki` is in the `slot`. |
| bool IsKeyInSlot(const std::vector<uint8_t> public_key_spki, |
| PK11SlotInfo* slot) { |
| scoped_refptr<ownership::OwnerKeyUtil> key_util = |
| base::MakeRefCounted<ownership::OwnerKeyUtilImpl>( |
| /*public_key_file=*/base::FilePath()); |
| return bool(key_util->FindPrivateKeyInSlot(public_key_spki, slot)); |
| } |
| |
| content::BrowserTaskEnvironment task_environment_; |
| ScopedTestingLocalState scoped_local_state_{ |
| TestingBrowserProcess::GetGlobal()}; |
| |
| std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_; |
| raw_ptr<ash::FakeChromeUserManager> user_manager_ = nullptr; |
| |
| const user_manager::UserType user_type_; |
| scoped_refptr<ownership::MockOwnerKeyUtil> owner_key_util_; |
| FakeSessionManagerClient session_manager_client_; |
| std::unique_ptr<TestingProfile> profile_; |
| raw_ptr<FakeNssService> nss_service_ = nullptr; |
| ash::DeviceSettingsService device_settings_service_; |
| std::unique_ptr<OwnerKeyLoader> key_loader_; |
| base::test::TestFuture<PublicKeyRefPtr, PrivateKeyRefPtr> result_observer_; |
| base::HistogramTester histogram_tester_; |
| }; |
| |
| class RegularOwnerKeyLoaderTest : public OwnerKeyLoaderTestBase { |
| public: |
| RegularOwnerKeyLoaderTest() |
| : OwnerKeyLoaderTestBase(user_manager::UserType::kRegular) {} |
| }; |
| |
| // Test that the first user generates a new owner key in the public slot (when |
| // the user is a regular user and the related experiment is disabled). |
| TEST_F(RegularOwnerKeyLoaderTest, FirstUserGeneratesOwnerKeyInPublicSlot) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitWithFeatures( |
| /*enabled_features=*/{}, |
| /*disabled_features=*/{kStoreOwnerKeyInPrivateSlot, |
| kMigrateOwnerKeyToPrivateSlot}); |
| |
| // In real code DeviceSettingsService must call this for the first user. |
| device_settings_service_.MarkWillEstablishConsumerOwnership(); |
| // Do not prepare any keys, so key_loader_ has to generate a new one. |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| EXPECT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| EXPECT_TRUE(IsKeyInSlot(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| nss_service_->GetPublicSlot())); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude( |
| Bucket(OwnerKeyUmaEvent::kEstablishingConsumerOwnershipSuccess, 1), |
| Bucket(OwnerKeyUmaEvent::kOwnerKeyGeneratedSuccess, 1), |
| Bucket(OwnerKeyUmaEvent::kPublicSlotKeyGenerationSuccess, 1))); |
| } |
| |
| // Test that the first user generates a new owner key in the private slot (when |
| // the user is a regular user and the related experiment is enabled). |
| TEST_F(RegularOwnerKeyLoaderTest, FirstUserGeneratesOwnerKeyInPrivateSlot) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitWithFeatures( |
| /*enabled_features=*/{kStoreOwnerKeyInPrivateSlot}, |
| /*disabled_features=*/{kMigrateOwnerKeyToPrivateSlot}); |
| |
| // In real code DeviceSettingsService must call this for the first user. |
| device_settings_service_.MarkWillEstablishConsumerOwnership(); |
| // Do not prepare any keys, so key_loader_ has to generate a new one. |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| EXPECT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| EXPECT_TRUE(IsKeyInSlot(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| nss_service_->GetPrivateSlot())); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude( |
| Bucket(OwnerKeyUmaEvent::kEstablishingConsumerOwnershipSuccess, 1), |
| Bucket(OwnerKeyUmaEvent::kOwnerKeyGeneratedSuccess, 1), |
| Bucket(OwnerKeyUmaEvent::kPrivateSlotKeyGenerationSuccess, 1))); |
| } |
| |
| // Test that the first user generates owner key after a crash. If during the |
| // first attempt Chrome crashes, on the next launch DeviceSettingsService won't |
| // indicate again that the consumer owners needs to be established. |
| // In such a case OwnerKeyLoader should read the identity of the owner from |
| // local state. |
| TEST_F(RegularOwnerKeyLoaderTest, FirstUserGeneratesOwnerKeyAfterCrash) { |
| // Populate local state data that OwnerKeyLoader should read. |
| user_manager_->RecordOwner( |
| AccountId::FromUserEmail(profile_->GetProfileUserName())); |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| EXPECT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude( |
| Bucket( |
| OwnerKeyUmaEvent::kRegeneratingOwnerKeyBasedOnLocalStateSuccess, |
| 1), |
| Bucket(OwnerKeyUmaEvent::kOwnerKeyGeneratedSuccess, 1))); |
| } |
| |
| // Test that the second user doesn't try to generate a new owner key. |
| TEST_F(RegularOwnerKeyLoaderTest, SecondUserDoesNotTakeOwnership) { |
| // In real code the first user would have created some device policies and |
| // saved the public owner key on disk. Emulate that. |
| auto signing_key = ConfigureExistingPolicies("owner@example.com"); |
| owner_key_util_->SetPublicKeyFromPrivateKey(*signing_key); |
| device_settings_service_.LoadImmediately(); // Reload policies. |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| ASSERT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| EXPECT_EQ(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| ExtractSpkiDer(signing_key)); |
| EXPECT_FALSE(result_observer_.Get<PrivateKeyRefPtr>()); |
| |
| EXPECT_THAT(histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude(Bucket( |
| OwnerKeyUmaEvent::kUserNotAnOwnerBasedOnPolicySuccess, 1))); |
| } |
| |
| // Test that an owner user gets recognized as the owner when it's mentioned in |
| // the existing device policies and owns the key in the public slot. |
| TEST_F(RegularOwnerKeyLoaderTest, OwnerUserLoadsExistingKeyFromPublicSlot) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitWithFeatures( |
| /*enabled_features=*/{kStoreOwnerKeyInPrivateSlot}, |
| /*disabled_features=*/{kMigrateOwnerKeyToPrivateSlot}); |
| |
| // Configure existing device policies and the owner key. |
| auto signing_key = ConfigureExistingPolicies(profile_->GetProfileUserName()); |
| owner_key_util_->ImportPrivateKeyInSlotAndSetPublicKey( |
| signing_key->Copy(), nss_service_->GetPublicSlot()); |
| device_settings_service_.LoadImmediately(); // Reload policies. |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| ASSERT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| EXPECT_EQ(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| ExtractSpkiDer(signing_key)); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude(Bucket(OwnerKeyUmaEvent::kOwnerHasKeysSuccess, 1), |
| Bucket(OwnerKeyUmaEvent::kOwnerKeyInPublicSlotTrue, 1))); |
| } |
| |
| // Test that an owner user gets recognized as the owner when it's mentioned in |
| // the existing device policies and owns the key in the private slot. |
| TEST_F(RegularOwnerKeyLoaderTest, OwnerUserLoadsExistingKeyFromPrivateSlot) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitWithFeatures( |
| /*enabled_features=*/{kStoreOwnerKeyInPrivateSlot}, |
| /*disabled_features=*/{kMigrateOwnerKeyToPrivateSlot}); |
| |
| // Configure existing device policies and the owner key. |
| auto signing_key = ConfigureExistingPolicies(profile_->GetProfileUserName()); |
| owner_key_util_->ImportPrivateKeyInSlotAndSetPublicKey( |
| signing_key->Copy(), nss_service_->GetPrivateSlot()); |
| device_settings_service_.LoadImmediately(); // Reload policies. |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| ASSERT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| EXPECT_EQ(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| ExtractSpkiDer(signing_key)); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude(Bucket(OwnerKeyUmaEvent::kOwnerHasKeysSuccess, 1), |
| Bucket(OwnerKeyUmaEvent::kOwnerKeyInPublicSlotFalse, 1))); |
| } |
| |
| // Test that even without existing device policies the owner key gets loaded |
| // from the public slot (that will help Chrome to recognize the current user as |
| // the owner). |
| TEST_F(RegularOwnerKeyLoaderTest, |
| OwnerUserLoadsExistingKeyFromPublicSlotWithoutPolicies) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitWithFeatures( |
| /*enabled_features=*/{kStoreOwnerKeyInPrivateSlot}, |
| /*disabled_features=*/{kMigrateOwnerKeyToPrivateSlot}); |
| |
| policy::DevicePolicyBuilder policy_builder; |
| auto signing_key = policy_builder.GetSigningKey(); |
| |
| owner_key_util_->ImportPrivateKeyInSlotAndSetPublicKey( |
| signing_key->Copy(), nss_service_->GetPublicSlot()); |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| ASSERT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| EXPECT_EQ(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| ExtractSpkiDer(signing_key)); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude(Bucket(OwnerKeyUmaEvent::kOwnerHasKeysSuccess, 1))); |
| } |
| |
| // Test that even without existing device policies the owner key gets loaded |
| // from the private slot (that will help Chrome to recognize the current user as |
| // the owner). |
| TEST_F(RegularOwnerKeyLoaderTest, |
| OwnerUserLoadsExistingKeyFromPrivateSlotWithoutPolicies) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitWithFeatures( |
| /*enabled_features=*/{kStoreOwnerKeyInPrivateSlot}, |
| /*disabled_features=*/{kMigrateOwnerKeyToPrivateSlot}); |
| |
| policy::DevicePolicyBuilder policy_builder; |
| auto signing_key = policy_builder.GetSigningKey(); |
| |
| owner_key_util_->ImportPrivateKeyInSlotAndSetPublicKey( |
| signing_key->Copy(), nss_service_->GetPrivateSlot()); |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| ASSERT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| EXPECT_EQ(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| ExtractSpkiDer(signing_key)); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude(Bucket(OwnerKeyUmaEvent::kOwnerHasKeysSuccess, 1))); |
| } |
| |
| // Test that the second user is not falsely recognized as the owner even if |
| // policies fail to load and does not have the owner key. |
| TEST_F(RegularOwnerKeyLoaderTest, SecondaryUserWithoutPolicies) { |
| policy::DevicePolicyBuilder policy_builder; |
| auto signing_key = policy_builder.GetSigningKey(); |
| |
| owner_key_util_->SetPublicKeyFromPrivateKey(*signing_key); |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| ASSERT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| EXPECT_EQ(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| ExtractSpkiDer(signing_key)); |
| EXPECT_FALSE(result_observer_.Get<PrivateKeyRefPtr>()); |
| |
| EXPECT_THAT(histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude( |
| Bucket(OwnerKeyUmaEvent::kUnsureUserNotAnOwnerSuccess, 1))); |
| } |
| |
| // Test that an owner user still gets recognized as the owner when it's |
| // mentioned in the existing device policies, but the owner key was lost. |
| // The key must be re-generated in such a case. |
| TEST_F(RegularOwnerKeyLoaderTest, |
| OwnerUserRegeneratesMissingKeyBasedOnPolicies) { |
| auto signing_key = ConfigureExistingPolicies(profile_->GetProfileUserName()); |
| // Configure that the public key is on disk, but the private key doesn't |
| // exist. |
| owner_key_util_->SetPublicKeyFromPrivateKey(*signing_key); |
| device_settings_service_.LoadImmediately(); // Reload policies. |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| ASSERT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| EXPECT_NE(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| ExtractSpkiDer(signing_key)); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude( |
| Bucket(OwnerKeyUmaEvent::kRegeneratingOwnerKeyBasedOnPolicySuccess, |
| 1), |
| Bucket(OwnerKeyUmaEvent::kOwnerKeyGeneratedSuccess, 1))); |
| } |
| |
| // Test that an owner user still gets recognized as the owner when it's |
| // mentioned in the local state, but the device policies and the owner key were |
| // lost. The key must be re-generated in such a case. |
| TEST_F(RegularOwnerKeyLoaderTest, |
| OwnerUserRegeneratesMissingKeyBasedOnLocalState) { |
| // Populate local state. |
| user_manager_->RecordOwner( |
| AccountId::FromUserEmail(profile_->GetProfileUserName())); |
| |
| policy::DevicePolicyBuilder policy_builder; |
| auto signing_key = policy_builder.GetSigningKey(); |
| |
| owner_key_util_->SetPublicKeyFromPrivateKey(*signing_key); |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| ASSERT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| EXPECT_NE(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| ExtractSpkiDer(signing_key)); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude( |
| // "Fail" means that the existence of the public key is unexpected. |
| Bucket(OwnerKeyUmaEvent::kRegeneratingOwnerKeyBasedOnLocalStateFail, |
| 1), |
| Bucket(OwnerKeyUmaEvent::kOwnerKeyGeneratedSuccess, 1))); |
| } |
| |
| // Test that OwnerKeyLoader makes several attempts to generate the owner key |
| // pair. |
| TEST_F(RegularOwnerKeyLoaderTest, KeyGenerationRetriedSuccessfully) { |
| // Make key_loader_ generate the key for the first user. |
| device_settings_service_.MarkWillEstablishConsumerOwnership(); |
| owner_key_util_->SimulateGenerateKeyFailure(/*fail_times=*/5); |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| ASSERT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude( |
| Bucket(OwnerKeyUmaEvent::kEstablishingConsumerOwnershipSuccess, 1), |
| // "Fail" means that there were generation errors before it succeeded. |
| Bucket(OwnerKeyUmaEvent::kOwnerKeyGeneratedFail, 1))); |
| } |
| |
| // Test that OwnerKeyLoader gives up to generate the owner key pair after a |
| // certain amount of attempts. |
| TEST_F(RegularOwnerKeyLoaderTest, KeyGenerationRetriedUnsuccessfully) { |
| // Make key_loader_ generate the key for the first user. |
| device_settings_service_.MarkWillEstablishConsumerOwnership(); |
| owner_key_util_->SimulateGenerateKeyFailure(/*fail_times=*/10); |
| |
| key_loader_->Run(); |
| |
| EXPECT_FALSE(result_observer_.Get<PublicKeyRefPtr>()); |
| EXPECT_FALSE(result_observer_.Get<PrivateKeyRefPtr>()); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude( |
| Bucket(OwnerKeyUmaEvent::kEstablishingConsumerOwnershipSuccess, 1), |
| Bucket(OwnerKeyUmaEvent::kFailedToGenerateOwnerKeyFail, 1))); |
| } |
| |
| // Test that enterprise devices don't attempt to load private key. The signing |
| // key of the device policy is owned by the backend server in the |
| // enterprise-enrolled case. |
| TEST_F(RegularOwnerKeyLoaderTest, EnterpriseDevicesDontNeedPrivateKey) { |
| // Create favorable conditions for the code to load private key. Claim in the |
| // device policies that the user is the owner (shouldn't happen on a real |
| // device) and prepare a private key in case OwnerKeyLoader tries to load it. |
| auto signing_key = ConfigureExistingPolicies(profile_->GetProfileUserName()); |
| owner_key_util_->ImportPrivateKeyAndSetPublicKey(signing_key->Copy()); |
| |
| // Re-create the loader with is_enterprise_enrolled=true. |
| key_loader_ = std::make_unique<OwnerKeyLoader>( |
| profile_.get(), &device_settings_service_, owner_key_util_, |
| /*is_enterprise_enrolled=*/true, result_observer_.GetCallback()); |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| ASSERT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| // Check that the private key wasn't loaded. |
| EXPECT_FALSE(result_observer_.Get<PrivateKeyRefPtr>()); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude(Bucket(OwnerKeyUmaEvent::kManagedDeviceSuccess, 1))); |
| } |
| |
| // Test that the owner key from the public slot is migrated into the private |
| // slot when the feature flags is enabled. |
| TEST_F(RegularOwnerKeyLoaderTest, MigrateFromPublicToPrivateSlot) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitWithFeatures( |
| /*enabled_features=*/{kStoreOwnerKeyInPrivateSlot, |
| kMigrateOwnerKeyToPrivateSlot}, |
| /*disabled_features=*/{}); |
| |
| // Configure existing device policies and the owner key. |
| auto signing_key = ConfigureExistingPolicies(profile_->GetProfileUserName()); |
| owner_key_util_->ImportPrivateKeyInSlotAndSetPublicKey( |
| signing_key->Copy(), nss_service_->GetPublicSlot()); |
| device_settings_service_.LoadImmediately(); // Reload policies. |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| ASSERT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| EXPECT_NE(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| ExtractSpkiDer(signing_key)); |
| EXPECT_EQ(ExtractSpkiDer(key_loader_->ExtractOldOwnerKey()), |
| ExtractSpkiDer(signing_key)); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| EXPECT_TRUE(IsKeyInSlot(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| nss_service_->GetPrivateSlot())); |
| |
| EXPECT_THAT(histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude( |
| Bucket(OwnerKeyUmaEvent::kOwnerHasKeysSuccess, 1), |
| Bucket(OwnerKeyUmaEvent::kOwnerKeyInPublicSlotTrue, 1), |
| Bucket(OwnerKeyUmaEvent::kOwnerKeyGeneratedSuccess, 1), |
| Bucket(OwnerKeyUmaEvent::kPrivateSlotKeyGenerationSuccess, 1), |
| Bucket(OwnerKeyUmaEvent::kMigrationToPrivateSlotStarted, 1))); |
| } |
| |
| // Test that the owner key from the public slot is not migrated when the feature |
| // flag is disabled. |
| TEST_F(RegularOwnerKeyLoaderTest, NotMigratedFromPublicToPrivateSlot) { |
| base::test::ScopedFeatureList feature_list; |
| // With this config Chrome should generate new keys in the private slot, but |
| // not migrate existing keys from the public slot. |
| feature_list.InitWithFeatures( |
| /*enabled_features=*/{kStoreOwnerKeyInPrivateSlot}, |
| /*disabled_features=*/{kMigrateOwnerKeyToPrivateSlot}); |
| |
| // Configure existing device policies and the owner key. |
| auto signing_key = ConfigureExistingPolicies(profile_->GetProfileUserName()); |
| owner_key_util_->ImportPrivateKeyInSlotAndSetPublicKey( |
| signing_key->Copy(), nss_service_->GetPublicSlot()); |
| device_settings_service_.LoadImmediately(); // Reload policies. |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| ASSERT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| EXPECT_EQ(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| ExtractSpkiDer(signing_key)); |
| EXPECT_EQ(key_loader_->ExtractOldOwnerKey(), nullptr); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| EXPECT_FALSE(IsKeyInSlot(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| nss_service_->GetPrivateSlot())); |
| |
| EXPECT_THAT(histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude( |
| Bucket(OwnerKeyUmaEvent::kOwnerHasKeysSuccess, 1), |
| Bucket(OwnerKeyUmaEvent::kOwnerKeyInPublicSlotTrue, 1), |
| Bucket(OwnerKeyUmaEvent::kMigrationToPrivateSlotStarted, 0))); |
| } |
| |
| // Test that the owner key from the private slot is not migrated back into the |
| // public slot when the feature flags are enabled. |
| TEST_F(RegularOwnerKeyLoaderTest, NotMigratedFromPrivateToPublicSlot) { |
| base::test::ScopedFeatureList feature_list; |
| // With this config Chrome should generate new keys in the private slot, but |
| // not migrate existing keys from the public slot. |
| feature_list.InitWithFeatures( |
| /*enabled_features=*/{kStoreOwnerKeyInPrivateSlot, |
| kMigrateOwnerKeyToPrivateSlot}, |
| /*disabled_features=*/{}); |
| |
| // Configure existing device policies and the owner key. |
| auto signing_key = ConfigureExistingPolicies(profile_->GetProfileUserName()); |
| owner_key_util_->ImportPrivateKeyInSlotAndSetPublicKey( |
| signing_key->Copy(), nss_service_->GetPrivateSlot()); |
| device_settings_service_.LoadImmediately(); // Reload policies. |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| ASSERT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| EXPECT_EQ(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| ExtractSpkiDer(signing_key)); |
| EXPECT_EQ(key_loader_->ExtractOldOwnerKey(), nullptr); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| EXPECT_FALSE(IsKeyInSlot(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| nss_service_->GetPublicSlot())); |
| |
| EXPECT_THAT(histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude( |
| Bucket(OwnerKeyUmaEvent::kOwnerHasKeysSuccess, 1), |
| Bucket(OwnerKeyUmaEvent::kOwnerKeyInPublicSlotFalse, 1), |
| Bucket(OwnerKeyUmaEvent::kMigrationToPrivateSlotStarted, 0))); |
| } |
| |
| // Test that the owner key from the private slot is migrated back into the |
| // public slot when the feature flags are disabled. |
| TEST_F(RegularOwnerKeyLoaderTest, MigrateFromPrivateToPublicSlot) { |
| base::test::ScopedFeatureList feature_list; |
| // With this config Chrome should generate new keys in the private slot, but |
| // not migrate existing keys from the public slot. |
| feature_list.InitWithFeatures( |
| /*enabled_features=*/{}, |
| /*disabled_features=*/{kStoreOwnerKeyInPrivateSlot, |
| kMigrateOwnerKeyToPrivateSlot}); |
| |
| // Configure existing device policies and the owner key. |
| auto signing_key = ConfigureExistingPolicies(profile_->GetProfileUserName()); |
| owner_key_util_->ImportPrivateKeyInSlotAndSetPublicKey( |
| signing_key->Copy(), nss_service_->GetPrivateSlot()); |
| device_settings_service_.LoadImmediately(); // Reload policies. |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| ASSERT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| EXPECT_NE(result_observer_.Get<PublicKeyRefPtr>()->data(), |
| ExtractSpkiDer(signing_key)); |
| EXPECT_EQ(ExtractSpkiDer(key_loader_->ExtractOldOwnerKey()), |
| ExtractSpkiDer(signing_key)); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| |
| EXPECT_THAT(histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude( |
| Bucket(OwnerKeyUmaEvent::kOwnerHasKeysSuccess, 1), |
| Bucket(OwnerKeyUmaEvent::kOwnerKeyInPublicSlotFalse, 1), |
| Bucket(OwnerKeyUmaEvent::kMigrationToPublicSlotStarted, 1))); |
| } |
| |
| // Test that OwnerKeyLoader silently exits if it was started after the shutdown |
| // had started. |
| TEST_F(RegularOwnerKeyLoaderTest, ExitOnShutdown) { |
| // In real code DeviceSettingsService must call this for the first user. |
| device_settings_service_.MarkWillEstablishConsumerOwnership(); |
| // Do not prepare any keys, so key_loader_ has to generate a new one. |
| TestingBrowserProcess::GetGlobal()->SetShuttingDown(true); |
| |
| key_loader_->Run(); |
| |
| EXPECT_FALSE(result_observer_.Get<PublicKeyRefPtr>()); |
| EXPECT_FALSE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_EQ(histogram_tester_.GetTotalSum(kOwnerKeyHistogramName), 0); |
| } |
| |
| class ChildOwnerKeyLoaderTest : public OwnerKeyLoaderTestBase { |
| public: |
| ChildOwnerKeyLoaderTest() |
| : OwnerKeyLoaderTestBase(user_manager::UserType::kChild) {} |
| }; |
| |
| // Test that the first user generates a new owner key (when the user is a |
| // child user). |
| TEST_F(ChildOwnerKeyLoaderTest, FirstUserGeneratesOwnerKey) { |
| // In real code DeviceSettingsService must call this for the first user. |
| device_settings_service_.MarkWillEstablishConsumerOwnership(); |
| // Do not prepare any keys, so key_loader_ has to generate a new one. |
| |
| key_loader_->Run(); |
| |
| ASSERT_TRUE(result_observer_.Get<PublicKeyRefPtr>()); |
| EXPECT_TRUE(!result_observer_.Get<PublicKeyRefPtr>()->is_empty()); |
| ASSERT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()); |
| EXPECT_TRUE(result_observer_.Get<PrivateKeyRefPtr>()->key()); |
| |
| EXPECT_THAT( |
| histogram_tester_.GetAllSamples(kOwnerKeyHistogramName), |
| BucketsInclude( |
| Bucket(OwnerKeyUmaEvent::kEstablishingConsumerOwnershipSuccess, 1), |
| Bucket(OwnerKeyUmaEvent::kOwnerKeyGeneratedSuccess, 1))); |
| } |
| |
| } // namespace ash |