chromium/chromeos/ash/services/device_sync/cryptauth_key_registry_impl_unittest.cc

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

#include "chromeos/ash/services/device_sync/cryptauth_key_registry_impl.h"

#include "base/containers/contains.h"
#include "chromeos/ash/services/device_sync/cryptauth_enrollment_constants.h"
#include "chromeos/ash/services/device_sync/pref_names.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash::device_sync {

class DeviceSyncCryptAuthKeyRegistryImplTest : public testing::Test {
 public:
  DeviceSyncCryptAuthKeyRegistryImplTest(
      const DeviceSyncCryptAuthKeyRegistryImplTest&) = delete;
  DeviceSyncCryptAuthKeyRegistryImplTest& operator=(
      const DeviceSyncCryptAuthKeyRegistryImplTest&) = delete;

 protected:
  DeviceSyncCryptAuthKeyRegistryImplTest() = default;

  ~DeviceSyncCryptAuthKeyRegistryImplTest() override = default;

  void SetUp() override {
    CryptAuthKeyRegistryImpl::RegisterPrefs(pref_service_.registry());
    key_registry_ = CryptAuthKeyRegistryImpl::Factory::Create(&pref_service_);
  }

  // Verify that changing the in-memory key bundle map updates the pref.
  void VerifyPrefValue(const base::Value::Dict& expected_dict) {
    const base::Value::Dict& dict =
        pref_service_.GetDict(prefs::kCryptAuthKeyRegistry);
    EXPECT_EQ(expected_dict, dict);
  }

  PrefService* pref_service() { return &pref_service_; }

  CryptAuthKeyRegistry* key_registry() { return key_registry_.get(); }

 private:
  TestingPrefServiceSimple pref_service_;

  std::unique_ptr<CryptAuthKeyRegistry> key_registry_;
};

TEST_F(DeviceSyncCryptAuthKeyRegistryImplTest, GetActiveKey_NoActiveKey) {
  CryptAuthKey sym_key("symmetric-key", CryptAuthKey::Status::kInactive,
                       cryptauthv2::KeyType::RAW256, "sym-handle");
  key_registry()->AddKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey, sym_key);

  EXPECT_FALSE(key_registry()->GetActiveKey(
      CryptAuthKeyBundle::Name::kLegacyAuthzenKey));
}

TEST_F(DeviceSyncCryptAuthKeyRegistryImplTest, GetActiveKey) {
  CryptAuthKey sym_key("symmetric-key", CryptAuthKey::Status::kInactive,
                       cryptauthv2::KeyType::RAW256, "sym-handle");
  CryptAuthKey asym_key("public-key", "private-key",
                        CryptAuthKey::Status::kActive,
                        cryptauthv2::KeyType::P256, "asym-handle");
  key_registry()->AddKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey, sym_key);
  key_registry()->AddKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey, asym_key);

  const CryptAuthKey* key =
      key_registry()->GetActiveKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey);
  ASSERT_TRUE(key);
  EXPECT_EQ(asym_key, *key);
}

TEST_F(DeviceSyncCryptAuthKeyRegistryImplTest, AddKey) {
  CryptAuthKey sym_key("symmetric-key", CryptAuthKey::Status::kActive,
                       cryptauthv2::KeyType::RAW256, "sym-handle");
  key_registry()->AddKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey, sym_key);
  const CryptAuthKeyBundle* key_bundle =
      key_registry()->GetKeyBundle(CryptAuthKeyBundle::Name::kLegacyAuthzenKey);
  ASSERT_TRUE(key_bundle);

  const CryptAuthKey* active_key =
      key_registry()->GetActiveKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey);
  ASSERT_TRUE(active_key);
  EXPECT_EQ(sym_key, *active_key);

  CryptAuthKeyBundle expected_bundle(
      CryptAuthKeyBundle::Name::kLegacyAuthzenKey);
  expected_bundle.AddKey(sym_key);
  EXPECT_EQ(expected_bundle, *key_bundle);

  base::Value::Dict expected_dict;
  expected_dict.Set(
      CryptAuthKeyBundle::KeyBundleNameEnumToString(expected_bundle.name()),
      expected_bundle.AsDictionary());
  VerifyPrefValue(expected_dict);

  // Add another key to same bundle
  CryptAuthKey asym_key("public-key", "private-key",
                        CryptAuthKey::Status::kActive,
                        cryptauthv2::KeyType::P256, "asym-handle");
  key_registry()->AddKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey, asym_key);

  expected_bundle.AddKey(asym_key);
  EXPECT_EQ(expected_bundle, *key_bundle);

  active_key =
      key_registry()->GetActiveKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey);
  ASSERT_TRUE(active_key);
  EXPECT_EQ(asym_key, *active_key);

  expected_dict.Set(
      CryptAuthKeyBundle::KeyBundleNameEnumToString(expected_bundle.name()),
      expected_bundle.AsDictionary());
  VerifyPrefValue(expected_dict);
}

TEST_F(DeviceSyncCryptAuthKeyRegistryImplTest, SetActiveKey) {
  CryptAuthKey sym_key("symmetric-key", CryptAuthKey::Status::kInactive,
                       cryptauthv2::KeyType::RAW256, "sym-handle");
  CryptAuthKey asym_key("public-key", "private-key",
                        CryptAuthKey::Status::kActive,
                        cryptauthv2::KeyType::P256, "asym-handle");
  key_registry()->AddKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey, sym_key);
  key_registry()->AddKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey, asym_key);

  key_registry()->SetActiveKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey,
                               "sym-handle");

  const CryptAuthKey* key =
      key_registry()->GetActiveKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey);
  EXPECT_TRUE(key);

  sym_key.set_status(CryptAuthKey::Status::kActive);
  EXPECT_EQ(sym_key, *key);

  CryptAuthKeyBundle expected_bundle(
      CryptAuthKeyBundle::Name::kLegacyAuthzenKey);
  expected_bundle.AddKey(sym_key);
  asym_key.set_status(CryptAuthKey::Status::kInactive);
  expected_bundle.AddKey(asym_key);
  base::Value::Dict expected_dict;
  expected_dict.Set(
      CryptAuthKeyBundle::KeyBundleNameEnumToString(expected_bundle.name()),
      expected_bundle.AsDictionary());
  VerifyPrefValue(expected_dict);
}

TEST_F(DeviceSyncCryptAuthKeyRegistryImplTest, DeactivateKeys) {
  CryptAuthKey sym_key("symmetric-key", CryptAuthKey::Status::kInactive,
                       cryptauthv2::KeyType::RAW256, "sym-handle");
  CryptAuthKey asym_key("public-key", "private-key",
                        CryptAuthKey::Status::kActive,
                        cryptauthv2::KeyType::P256, "asym-handle");
  key_registry()->AddKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey, sym_key);
  key_registry()->AddKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey, asym_key);

  key_registry()->DeactivateKeys(CryptAuthKeyBundle::Name::kLegacyAuthzenKey);

  EXPECT_FALSE(key_registry()->GetActiveKey(
      CryptAuthKeyBundle::Name::kLegacyAuthzenKey));

  CryptAuthKeyBundle expected_bundle(
      CryptAuthKeyBundle::Name::kLegacyAuthzenKey);
  expected_bundle.AddKey(sym_key);
  asym_key.set_status(CryptAuthKey::Status::kInactive);
  expected_bundle.AddKey(asym_key);
  base::Value::Dict expected_dict;
  expected_dict.Set(
      CryptAuthKeyBundle::KeyBundleNameEnumToString(expected_bundle.name()),
      expected_bundle.AsDictionary());
  VerifyPrefValue(expected_dict);
}

TEST_F(DeviceSyncCryptAuthKeyRegistryImplTest, DeleteKey) {
  CryptAuthKey sym_key("symmetric-key", CryptAuthKey::Status::kInactive,
                       cryptauthv2::KeyType::RAW256, "sym-handle");
  CryptAuthKey asym_key("public-key", "private-key",
                        CryptAuthKey::Status::kActive,
                        cryptauthv2::KeyType::P256, "asym-handle");
  key_registry()->AddKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey, sym_key);
  key_registry()->AddKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey, asym_key);

  key_registry()->DeleteKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey,
                            "sym-handle");

  const CryptAuthKeyBundle* key_bundle =
      key_registry()->GetKeyBundle(CryptAuthKeyBundle::Name::kLegacyAuthzenKey);
  ASSERT_TRUE(key_bundle);

  EXPECT_FALSE(base::Contains(key_bundle->handle_to_key_map(), "sym-handle"));
  EXPECT_TRUE(base::Contains(key_bundle->handle_to_key_map(), "asym-handle"));

  CryptAuthKeyBundle expected_bundle(
      CryptAuthKeyBundle::Name::kLegacyAuthzenKey);
  expected_bundle.AddKey(asym_key);
  base::Value::Dict expected_dict;
  expected_dict.Set(
      CryptAuthKeyBundle::KeyBundleNameEnumToString(expected_bundle.name()),
      expected_bundle.AsDictionary());
  VerifyPrefValue(expected_dict);
}

TEST_F(DeviceSyncCryptAuthKeyRegistryImplTest, SetKeyDirective) {
  CryptAuthKey sym_key("symmetric-key", CryptAuthKey::Status::kInactive,
                       cryptauthv2::KeyType::RAW256, "sym-handle");
  key_registry()->AddKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey, sym_key);

  cryptauthv2::KeyDirective key_directive;
  key_directive.set_enroll_time_millis(1000);
  key_registry()->SetKeyDirective(CryptAuthKeyBundle::Name::kLegacyAuthzenKey,
                                  key_directive);

  const CryptAuthKeyBundle* key_bundle =
      key_registry()->GetKeyBundle(CryptAuthKeyBundle::Name::kLegacyAuthzenKey);
  ASSERT_TRUE(key_bundle);

  EXPECT_TRUE(key_bundle->key_directive());
  EXPECT_EQ(key_directive.SerializeAsString(),
            key_bundle->key_directive()->SerializeAsString());

  CryptAuthKeyBundle expected_bundle(
      CryptAuthKeyBundle::Name::kLegacyAuthzenKey);
  expected_bundle.AddKey(sym_key);
  expected_bundle.set_key_directive(key_directive);
  base::Value::Dict expected_dict;
  expected_dict.Set(
      CryptAuthKeyBundle::KeyBundleNameEnumToString(expected_bundle.name()),
      expected_bundle.AsDictionary());
  VerifyPrefValue(expected_dict);
}

TEST_F(DeviceSyncCryptAuthKeyRegistryImplTest,
       ConstructorPopulatesBundlesUsingPref) {
  CryptAuthKey asym_key(
      "public-key", "private-key", CryptAuthKey::Status::kActive,
      cryptauthv2::KeyType::P256, kCryptAuthFixedUserKeyPairHandle);
  key_registry()->AddKey(CryptAuthKeyBundle::Name::kUserKeyPair, asym_key);

  CryptAuthKey sym_key("symmetric-key", CryptAuthKey::Status::kActive,
                       cryptauthv2::KeyType::RAW256, "sym-handle");
  cryptauthv2::KeyDirective key_directive;
  key_directive.set_enroll_time_millis(1000);
  key_registry()->AddKey(CryptAuthKeyBundle::Name::kLegacyAuthzenKey, sym_key);
  key_registry()->SetKeyDirective(CryptAuthKeyBundle::Name::kLegacyAuthzenKey,
                                  key_directive);

  // A new registry using the same pref service that was just written.
  std::unique_ptr<CryptAuthKeyRegistry> new_registry =
      CryptAuthKeyRegistryImpl::Factory::Create(pref_service());

  EXPECT_EQ(2u, new_registry->key_bundles().size());

  const CryptAuthKeyBundle* key_bundle_user_key_pair =
      key_registry()->GetKeyBundle(CryptAuthKeyBundle::Name::kUserKeyPair);
  ASSERT_TRUE(key_bundle_user_key_pair);
  CryptAuthKeyBundle expected_bundle_user_key_pair(
      CryptAuthKeyBundle::Name::kUserKeyPair);
  expected_bundle_user_key_pair.AddKey(asym_key);
  EXPECT_EQ(expected_bundle_user_key_pair, *key_bundle_user_key_pair);

  const CryptAuthKeyBundle* key_bundle_legacy_authzen_key =
      key_registry()->GetKeyBundle(CryptAuthKeyBundle::Name::kLegacyAuthzenKey);
  ASSERT_TRUE(key_bundle_legacy_authzen_key);
  CryptAuthKeyBundle expected_bundle_legacy_authzen_key(
      CryptAuthKeyBundle::Name::kLegacyAuthzenKey);
  expected_bundle_legacy_authzen_key.AddKey(sym_key);
  expected_bundle_legacy_authzen_key.set_key_directive(key_directive);
  EXPECT_EQ(expected_bundle_legacy_authzen_key, *key_bundle_legacy_authzen_key);
}

}  // namespace ash::device_sync