chromium/chrome/browser/ash/platform_keys/key_permissions/key_permissions_service_impl_unittest.cc

// Copyright 2020 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/platform_keys/key_permissions/key_permissions_service_impl.h"

#include <stdint.h>

#include <memory>
#include <optional>
#include <vector>

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/test_future.h"
#include "chrome/browser/ash/platform_keys/key_permissions/key_permissions_manager_impl.h"
#include "chrome/browser/ash/platform_keys/key_permissions/mock_key_permissions_manager.h"
#include "chrome/browser/ash/platform_keys/mock_platform_keys_service.h"
#include "chrome/browser/ash/platform_keys/platform_keys_service.h"
#include "chrome/browser/ash/platform_keys/platform_keys_service_test_util.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {
namespace platform_keys {
namespace {

using ::chromeos::platform_keys::Status;
using ::chromeos::platform_keys::TokenId;
using ::testing::_;

// Supports waiting for the result of KeyPermissionsService::IsCorporateKey.
class IsCorporateKeyExecutionWaiter
    : public base::test::TestFuture<std::optional<bool>, Status> {
 public:
  bool corporate() { return Get<0>().value(); }
  Status status() { return Get<1>(); }
};

}  // namespace

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

  void SetUp() override {
    profile_ = std::make_unique<TestingProfile>();

    platform_keys_service_ = std::make_unique<MockPlatformKeysService>();
    user_token_key_permissions_manager_ =
        std::make_unique<MockKeyPermissionsManager>();

    key_permissions_service_ = std::make_unique<KeyPermissionsServiceImpl>(
        /*is_regular_profile=*/true, /*profile_is_managed=*/true,
        platform_keys_service_.get(),
        user_token_key_permissions_manager_.get());

    // All test keys that reside on user token only are not marked as corporate
    // by default unless specified.
    EXPECT_CALL(*user_token_key_permissions_manager_,
                IsKeyAllowedForUsage(_, _, _))
        .WillRepeatedly(base::test::RunOnceCallbackRepeatedly<0>(
            /*allowed=*/false, Status::kSuccess));

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

    // All test keys that reside on system token are marked for corporate usage
    // by default unless specified.
    EXPECT_CALL(*system_token_key_permissions_manager_,
                IsKeyAllowedForUsage(_, _, _))
        .WillRepeatedly(base::test::RunOnceCallbackRepeatedly<0>(
            /*allowed=*/true, Status::kSuccess));

    platform_keys::KeyPermissionsManagerImpl::
        SetSystemTokenKeyPermissionsManagerForTesting(
            system_token_key_permissions_manager_.get());
  }

 protected:
  void SetKeyLocations(const std::vector<uint8_t>& public_key,
                       const std::vector<TokenId>& key_locations) {
    ON_CALL(*platform_keys_service_, GetKeyLocations(public_key, _))
        .WillByDefault(testing::Invoke(
            [key_locations](std::vector<uint8_t> public_key_spki_der,
                            GetKeyLocationsCallback callback) {
              std::move(callback).Run(std::move(key_locations),
                                      Status::kSuccess);
            }));
  }

  bool IsCorporateKey(std::vector<uint8_t> public_key) const {
    IsCorporateKeyExecutionWaiter is_corporate_key_waiter;
    key_permissions_service_->IsCorporateKey(
        std::move(public_key), is_corporate_key_waiter.GetCallback());
    EXPECT_TRUE(is_corporate_key_waiter.Wait());
    EXPECT_EQ(is_corporate_key_waiter.status(), Status::kSuccess);
    return is_corporate_key_waiter.corporate();
  }

  void SetCorporateKey(const std::vector<uint8_t>& public_key) {
    EXPECT_CALL(*user_token_key_permissions_manager_,
                AllowKeyForUsage(_, KeyUsage::kCorporate, public_key))
        .Times(1)
        .WillOnce(base::test::RunOnceCallback<0>(Status::kSuccess));
    EXPECT_CALL(*user_token_key_permissions_manager_,
                IsKeyAllowedForUsage(_, KeyUsage::kCorporate, public_key))
        .WillOnce(
            base::test::RunOnceCallback<0>(/*allowed=*/true, Status::kSuccess));
    test_util::StatusWaiter set_corporate_key_waiter;

    key_permissions_service_->SetCorporateKey(
        std::vector<uint8_t>(public_key.begin(), public_key.end()),
        set_corporate_key_waiter.GetCallback());
    EXPECT_TRUE(set_corporate_key_waiter.Wait());
  }

  content::BrowserTaskEnvironment task_environment_;
  std::unique_ptr<TestingProfile> profile_;

  std::unique_ptr<KeyPermissionsServiceImpl> key_permissions_service_;
  std::unique_ptr<MockPlatformKeysService> platform_keys_service_;
  std::unique_ptr<MockKeyPermissionsManager>
      user_token_key_permissions_manager_;
  std::unique_ptr<MockKeyPermissionsManager>
      system_token_key_permissions_manager_;
};

TEST_F(KeyPermissionsServiceImplTest, SystemTokenKeyIsImplicitlyCorporate) {
  const std::vector<uint8_t> kPublicKey{1, 2, 3, 4, 5};

  SetKeyLocations(kPublicKey, {TokenId::kSystem});
  EXPECT_TRUE(IsCorporateKey(kPublicKey));

  SetKeyLocations(kPublicKey, {TokenId::kUser, TokenId::kSystem});
  EXPECT_TRUE(IsCorporateKey(kPublicKey));
}

TEST_F(KeyPermissionsServiceImplTest, CorporateRoundTrip) {
  const std::vector<uint8_t> kPublicKey{1, 2, 3, 4, 5};

  // By default, user-token keys are not corporate.
  SetKeyLocations(kPublicKey, {TokenId::kUser});
  EXPECT_FALSE(IsCorporateKey(kPublicKey));

  SetCorporateKey(kPublicKey);
  EXPECT_TRUE(IsCorporateKey(kPublicKey));

  // Check that a repeated call doesn't corrupt the stored state.
  SetCorporateKey(kPublicKey);
  EXPECT_TRUE(IsCorporateKey(kPublicKey));
}

}  // namespace platform_keys
}  // namespace ash