chromium/chrome/browser/ash/system_token_cert_db_initializer_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/system_token_cert_db_initializer.h"

#include <memory>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
#include "chromeos/ash/components/network/network_cert_loader.h"
#include "chromeos/ash/components/network/system_token_cert_db_storage_test_util.h"
#include "chromeos/ash/components/tpm/tpm_token_loader.h"
#include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
#include "content/public/test/browser_task_environment.h"
#include "crypto/nss_util.h"
#include "crypto/scoped_test_system_nss_key_slot.h"
#include "net/cert/nss_cert_database.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {

class SystemTokenCertDbInitializerTest : public testing::Test {
 public:
  SystemTokenCertDbInitializerTest() {
    TPMTokenLoader::InitializeForTest();
    UserDataAuthClient::InitializeFake();
    SystemTokenCertDbStorage::Initialize();
    NetworkCertLoader::Initialize();
    chromeos::TpmManagerClient::InitializeFake();

    system_token_cert_db_initializer_ =
        std::make_unique<SystemTokenCertDBInitializer>();
  }

  SystemTokenCertDbInitializerTest(
      const SystemTokenCertDbInitializerTest& other) = delete;
  SystemTokenCertDbInitializerTest& operator=(
      const SystemTokenCertDbInitializerTest& other) = delete;

  ~SystemTokenCertDbInitializerTest() override {
    system_token_cert_db_initializer_.reset();

    chromeos::TpmManagerClient::Shutdown();
    NetworkCertLoader::Shutdown();
    SystemTokenCertDbStorage::Shutdown();
    UserDataAuthClient::Shutdown();
    TPMTokenLoader::Shutdown();
  }

 protected:
  bool InitializeTestSystemSlot() {
    test_system_slot_ = std::make_unique<crypto::ScopedTestSystemNSSKeySlot>(
        /*simulate_token_loader=*/true);
    return test_system_slot_->ConstructedSuccessfully();
  }

  SystemTokenCertDBInitializer* system_token_cert_db_initializer() {
    return system_token_cert_db_initializer_.get();
  }

  base::test::TaskEnvironment* task_environment() { return &task_environment_; }

 private:
  content::BrowserTaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};

  std::unique_ptr<SystemTokenCertDBInitializer>
      system_token_cert_db_initializer_;
  std::unique_ptr<crypto::ScopedTestSystemNSSKeySlot> test_system_slot_;
};

// Tests that the system token certificate database will be returned
// successfully by SystemTokenCertDbInitializer if it was available in less than
// 5 minutes after being requested, and the system slot uses software fallback
// when it's allowed and TPM is disabled.
TEST_F(SystemTokenCertDbInitializerTest, GetDatabaseSuccessSoftwareFallback) {
  chromeos::TpmManagerClient::Get()
      ->GetTestInterface()
      ->mutable_nonsensitive_status_reply()
      ->set_is_enabled(false);
  chromeos::TpmManagerClient::Get()
      ->GetTestInterface()
      ->mutable_nonsensitive_status_reply()
      ->set_is_owned(false);
  system_token_cert_db_initializer()
      ->set_is_nss_slots_software_fallback_allowed_for_testing(true);

  GetSystemTokenCertDbCallbackWrapper get_system_token_cert_db_callback_wrapper;
  SystemTokenCertDbStorage::Get()->GetDatabase(
      get_system_token_cert_db_callback_wrapper.GetCallback());
  EXPECT_FALSE(get_system_token_cert_db_callback_wrapper.IsCallbackCalled());

  // Check that after 1 minute, SystemTokenCertDBInitializer is still waiting
  // for the system token slot to be initialized and the DB retrieval hasn't
  // timed out yet.
  const auto kOneMinuteDelay = base::Minutes(1);
  EXPECT_LT(kOneMinuteDelay,
            SystemTokenCertDBInitializer::kMaxCertDbRetrievalDelay);

  task_environment()->FastForwardBy(kOneMinuteDelay);
  EXPECT_FALSE(get_system_token_cert_db_callback_wrapper.IsCallbackCalled());

  EXPECT_TRUE(InitializeTestSystemSlot());
  get_system_token_cert_db_callback_wrapper.Wait();

  EXPECT_TRUE(get_system_token_cert_db_callback_wrapper.IsCallbackCalled());
  EXPECT_TRUE(
      get_system_token_cert_db_callback_wrapper.IsDbRetrievalSucceeded());
}

// Tests that the system token certificate database will be not returned
// successfully by SystemTokenCertDbInitializer if TPM is disabled and system
// slot software fallback is not allowed.
TEST_F(SystemTokenCertDbInitializerTest, GetDatabaseFailureDisabledTPM) {
  chromeos::TpmManagerClient::Get()
      ->GetTestInterface()
      ->mutable_nonsensitive_status_reply()
      ->set_is_enabled(false);
  chromeos::TpmManagerClient::Get()
      ->GetTestInterface()
      ->mutable_nonsensitive_status_reply()
      ->set_is_owned(false);
  system_token_cert_db_initializer()
      ->set_is_nss_slots_software_fallback_allowed_for_testing(false);

  GetSystemTokenCertDbCallbackWrapper get_system_token_cert_db_callback_wrapper;
  SystemTokenCertDbStorage::Get()->GetDatabase(
      get_system_token_cert_db_callback_wrapper.GetCallback());
  EXPECT_FALSE(get_system_token_cert_db_callback_wrapper.IsCallbackCalled());

  // Check that after 1 minute, SystemTokenCertDBInitializer is still waiting
  // for the system token slot to be initialized and the DB retrieval hasn't
  // timed out yet.
  const auto kOneMinuteDelay = base::Minutes(1);
  EXPECT_LT(kOneMinuteDelay,
            SystemTokenCertDBInitializer::kMaxCertDbRetrievalDelay);

  task_environment()->FastForwardBy(kOneMinuteDelay);
  EXPECT_FALSE(get_system_token_cert_db_callback_wrapper.IsCallbackCalled());

  EXPECT_TRUE(InitializeTestSystemSlot());
  get_system_token_cert_db_callback_wrapper.Wait();

  EXPECT_TRUE(get_system_token_cert_db_callback_wrapper.IsCallbackCalled());
  EXPECT_FALSE(
      get_system_token_cert_db_callback_wrapper.IsDbRetrievalSucceeded());
}

}  // namespace ash