chromium/chromeos/ash/components/network/system_token_cert_db_storage_unittest.cc

// Copyright 2021 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/components/network/system_token_cert_db_storage.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/network/system_token_cert_db_storage_test_util.h"
#include "crypto/scoped_test_nss_db.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 SystemTokenCertDbStorageTest : public testing::Test {
 public:
  SystemTokenCertDbStorageTest()
      : test_nssdb_(std::make_unique<crypto::ScopedTestNSSDB>()) {
    SystemTokenCertDbStorage::Initialize();
  }

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

  ~SystemTokenCertDbStorageTest() override {
    SystemTokenCertDbStorage::Shutdown();
  }

  void SetUp() override { ASSERT_TRUE(SystemTokenCertDbStorage::Get()); }

 protected:
  void SetSystemSlotInStorage() {
    test_cert_database_ = std::make_unique<net::NSSCertDatabase>(
        /*public_slot=*/crypto::ScopedPK11Slot(
            PK11_ReferenceSlot(test_nssdb_->slot())),
        /*private_slot=*/crypto::ScopedPK11Slot(
            PK11_ReferenceSlot(test_nssdb_->slot())));
    SystemTokenCertDbStorage::Get()->SetDatabase(test_cert_database_.get());
  }

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

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

  std::unique_ptr<crypto::ScopedTestNSSDB> test_nssdb_;
  std::unique_ptr<net::NSSCertDatabase> test_cert_database_;
};

// Tests that the system token certificate database will be returned
// successfully by SystemTokenCertDbStorage if it was available in less than
// 5 minutes after being requested.
TEST_F(SystemTokenCertDbStorageTest, GetDatabaseSuccess) {
  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, SystemTokenCertDbStorage 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,
            SystemTokenCertDbStorage::kMaxCertDbRetrievalDelay);

  task_environment()->FastForwardBy(kOneMinuteDelay);

  SetSystemSlotInStorage();

  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 returned
// successfully by SystemTokenCertDbInitializer if it was available in less
// than 5 minutes after being requested even if the slot was available after
// more than 5 minutes from the initialization of
// SystemTokenCertDbInitializer.
TEST_F(SystemTokenCertDbStorageTest, GetDatabaseLateRequestSuccess) {
  // Simulate waiting for 6 minutes after the initialization of the
  // SystemTokenCertDbStorage.
  const auto kSixMinuteDelay = base::Minutes(6);
  EXPECT_GT(kSixMinuteDelay,
            SystemTokenCertDbStorage::kMaxCertDbRetrievalDelay);
  task_environment()->FastForwardBy(kSixMinuteDelay);

  SetSystemSlotInStorage();

  GetSystemTokenCertDbCallbackWrapper get_system_token_cert_db_callback_wrapper;
  SystemTokenCertDbStorage::Get()->GetDatabase(
      get_system_token_cert_db_callback_wrapper.GetCallback());
  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 retrieval will fail if the
// system token initialization doesn't succeed within 5 minutes from the first
// database request.
TEST_F(SystemTokenCertDbStorageTest, GetDatabaseTimeout) {
  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());

  const auto kDelay1 = base::Minutes(2);
  EXPECT_LT(kDelay1, SystemTokenCertDbStorage::kMaxCertDbRetrievalDelay);

  const auto kDelay2 =
      SystemTokenCertDbStorage::kMaxCertDbRetrievalDelay - kDelay1;

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

  task_environment()->FastForwardBy(kDelay2);
  EXPECT_TRUE(get_system_token_cert_db_callback_wrapper.IsCallbackCalled());
  EXPECT_FALSE(
      get_system_token_cert_db_callback_wrapper.IsDbRetrievalSucceeded());
}

// Tests that if one of the system token certificate database requests timed
// out, following requests will fail as well.
TEST_F(SystemTokenCertDbStorageTest, GetDatabaseTimeoutMultipleRequests) {
  GetSystemTokenCertDbCallbackWrapper
      get_system_token_cert_db_callback_wrapper_1;
  SystemTokenCertDbStorage::Get()->GetDatabase(
      get_system_token_cert_db_callback_wrapper_1.GetCallback());
  EXPECT_FALSE(get_system_token_cert_db_callback_wrapper_1.IsCallbackCalled());

  task_environment()->FastForwardBy(
      SystemTokenCertDbStorage::kMaxCertDbRetrievalDelay);
  EXPECT_TRUE(get_system_token_cert_db_callback_wrapper_1.IsCallbackCalled());
  EXPECT_FALSE(
      get_system_token_cert_db_callback_wrapper_1.IsDbRetrievalSucceeded());

  GetSystemTokenCertDbCallbackWrapper
      get_system_token_cert_db_callback_wrapper_2;
  SystemTokenCertDbStorage::Get()->GetDatabase(
      get_system_token_cert_db_callback_wrapper_2.GetCallback());
  EXPECT_TRUE(get_system_token_cert_db_callback_wrapper_2.IsCallbackCalled());
  EXPECT_FALSE(
      get_system_token_cert_db_callback_wrapper_2.IsDbRetrievalSucceeded());
}

// Tests that calling ResetDatabase notifies the observers.
TEST_F(SystemTokenCertDbStorageTest, ResetDatabaseNotifiesObservers) {
  // Successfully request the database.
  GetSystemTokenCertDbCallbackWrapper get_system_token_cert_db_callback_wrapper;
  SystemTokenCertDbStorage::Get()->GetDatabase(
      get_system_token_cert_db_callback_wrapper.GetCallback());
  SetSystemSlotInStorage();
  get_system_token_cert_db_callback_wrapper.Wait();
  EXPECT_TRUE(get_system_token_cert_db_callback_wrapper.IsCallbackCalled());

  // When the database is reset, observers are notified about this.
  FakeSystemTokenCertDbStorageObserver observer;
  SystemTokenCertDbStorage::Get()->AddObserver(&observer);

  SystemTokenCertDbStorage::Get()->ResetDatabase();

  EXPECT_TRUE(observer.HasBeenNotified());
  SystemTokenCertDbStorage::Get()->RemoveObserver(&observer);
}

// Tests that requesting a database after it has been reset fails.
TEST_F(SystemTokenCertDbStorageTest, RequestingDatabaseFailsAfterReset) {
  // Successfully request the database.
  GetSystemTokenCertDbCallbackWrapper get_system_token_cert_db_callback_wrapper;
  SystemTokenCertDbStorage::Get()->GetDatabase(
      get_system_token_cert_db_callback_wrapper.GetCallback());
  SetSystemSlotInStorage();
  get_system_token_cert_db_callback_wrapper.Wait();
  EXPECT_TRUE(get_system_token_cert_db_callback_wrapper.IsCallbackCalled());

  // Now reset the database.
  SystemTokenCertDbStorage::Get()->ResetDatabase();

  // Requesting the database after that results in a failure.
  GetSystemTokenCertDbCallbackWrapper
      get_system_token_cert_db_callback_wrapper_2;
  SystemTokenCertDbStorage::Get()->GetDatabase(
      get_system_token_cert_db_callback_wrapper_2.GetCallback());
  EXPECT_TRUE(get_system_token_cert_db_callback_wrapper_2.IsCallbackCalled());
  EXPECT_FALSE(
      get_system_token_cert_db_callback_wrapper_2.IsDbRetrievalSucceeded());
}

}  // namespace ash