Commit 866b893a authored by Omar Morsi's avatar Omar Morsi Committed by Commit Bot

Add tests for system token DB retrieval timeout

This CL adds unit tests to test the behavior of
SystemTokenCertDBInitializer::GetSystemTokenCertDb when system token
initialization fails/succeeds.

Bug: 1068548, 844022
Change-Id: I2d7a0d5dabc74e768ed77f32ddfe48132ca6aa6c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2544569
Commit-Queue: Omar Morsi <omorsi@google.com>
Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Reviewed-by: default avatarMaksim Ivanov <emaxx@chromium.org>
Cr-Commit-Position: refs/heads/master@{#828880}
parent 6139e0c3
......@@ -3846,6 +3846,7 @@ source_set("unit_tests") {
"system_logs/shill_log_source_unittest.cc",
"system_logs/single_debug_daemon_log_source_unittest.cc",
"system_logs/single_log_file_log_source_unittest.cc",
"system_token_cert_db_initializer_unittest.cc",
"tether/tether_service_unittest.cc",
"throttle_observer_unittest.cc",
"throttle_service_unittest.cc",
......
......@@ -151,8 +151,6 @@ void DidGetCertDbOnUiThread(base::Optional<TokenId> token_id,
// Asynchronously fetches the NSSCertDatabase using |delegate| and, if
// |token_id| is not empty, the slot for |token_id|. Stores the slot in |state|
// and passes the database to |callback|. Will run |callback| on the IO thread.
// TODO(omorsi): Introduce timeout for retrieving certificate database in
// platform keys.
void GetCertDatabase(base::Optional<TokenId> token_id,
GetCertDBCallback callback,
PlatformKeysServiceImplDelegate* delegate,
......
......@@ -30,12 +30,6 @@ namespace chromeos {
namespace {
// It is stated in cryptohome implementation that 5 minutes is enough time to
// wait for any TPM operations. For more information, please refer to:
// https://chromium.googlesource.com/chromiumos/platform2/+/master/cryptohome/cryptohome.cc
constexpr base::TimeDelta kMaxCertDbRetrievalDelay =
base::TimeDelta::FromMinutes(5);
// Called on UI Thread when the system slot has been retrieved.
void GotSystemSlotOnUIThread(
base::OnceCallback<void(crypto::ScopedPK11Slot)> callback_ui_thread,
......@@ -86,6 +80,9 @@ SystemTokenCertDBInitializer* g_system_token_cert_db_initializer = nullptr;
} // namespace
constexpr base::TimeDelta
SystemTokenCertDBInitializer::kMaxCertDbRetrievalDelay;
SystemTokenCertDBInitializer::SystemTokenCertDBInitializer() {
// Only start loading the system token once cryptohome is available and only
// if the TPM is ready (available && owned && not being owned).
......
......@@ -16,6 +16,7 @@
#include "base/observer_list_types.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chromeos/dbus/cryptohome/cryptohome_client.h"
#include "crypto/scoped_nss_types.h"
......@@ -46,6 +47,12 @@ class SystemTokenCertDBObserver : public base::CheckedObserver {
// All of the methods must be called on the UI thread.
class SystemTokenCertDBInitializer final : public CryptohomeClient::Observer {
public:
// It is stated in cryptohome implementation that 5 minutes is enough time to
// wait for any TPM operations. For more information, please refer to:
// https://chromium.googlesource.com/chromiumos/platform2/+/master/cryptohome/cryptohome.cc
static constexpr base::TimeDelta kMaxCertDbRetrievalDelay =
base::TimeDelta::FromMinutes(5);
SystemTokenCertDBInitializer();
~SystemTokenCertDBInitializer() override;
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/system_token_cert_db_initializer.h"
#include <memory>
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "chromeos/dbus/cryptohome/cryptohome_client.h"
#include "chromeos/network/network_cert_loader.h"
#include "chromeos/tpm/tpm_token_loader.h"
#include "content/public/test/browser_task_environment.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 chromeos {
namespace {
// A helper that wraps the callback passed to
// SystemTokenCertDBInitializer::GetSystemTokenCertDb and can answer queries
// regarding the state of the callback and database passed to the callback.
class GetSystemTokenCertDbCallbackWrapper {
public:
GetSystemTokenCertDbCallbackWrapper() = default;
GetSystemTokenCertDbCallbackWrapper(
const GetSystemTokenCertDbCallbackWrapper& other) = delete;
GetSystemTokenCertDbCallbackWrapper& operator=(
const GetSystemTokenCertDbCallbackWrapper& other) = delete;
~GetSystemTokenCertDbCallbackWrapper() = default;
SystemTokenCertDBInitializer::GetSystemTokenCertDbCallback GetCallback() {
return base::BindOnce(&GetSystemTokenCertDbCallbackWrapper::OnDbRetrieved,
weak_ptr_factory_.GetWeakPtr());
}
// Waits until the callback returned by GetCallback() has been called.
void Wait() { run_loop_.Run(); }
bool IsCallbackCalled() { return done_; }
bool IsDbRetrievalSucceeded() { return nss_cert_database_ != nullptr; }
private:
void OnDbRetrieved(net::NSSCertDatabase* nss_cert_database) {
EXPECT_FALSE(done_);
done_ = true;
nss_cert_database_ = nss_cert_database;
run_loop_.Quit();
}
base::RunLoop run_loop_;
bool done_ = false;
net::NSSCertDatabase* nss_cert_database_ = nullptr;
base::WeakPtrFactory<GetSystemTokenCertDbCallbackWrapper> weak_ptr_factory_{
this};
};
} // namespace
class SystemTokenCertDbInitializerTest : public testing::Test {
public:
SystemTokenCertDbInitializerTest() {
TPMTokenLoader::InitializeForTest();
CryptohomeClient::InitializeFake();
NetworkCertLoader::Initialize();
system_token_cert_db_initializer_ =
std::make_unique<SystemTokenCertDBInitializer>();
}
SystemTokenCertDbInitializerTest(
const SystemTokenCertDbInitializerTest& other) = delete;
SystemTokenCertDbInitializerTest& operator=(
const SystemTokenCertDbInitializerTest& other) = delete;
~SystemTokenCertDbInitializerTest() override {
NetworkCertLoader::Shutdown();
CryptohomeClient::Shutdown();
TPMTokenLoader::Shutdown();
}
protected:
bool InitializeTestSystemSlot() {
test_system_slot_ = std::make_unique<crypto::ScopedTestSystemNSSKeySlot>();
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.
TEST_F(SystemTokenCertDbInitializerTest, GetSystemTokenCertDbSuccess) {
GetSystemTokenCertDbCallbackWrapper get_system_token_cert_db_callback_wrapper;
system_token_cert_db_initializer()->GetSystemTokenCertDb(
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::TimeDelta::FromMinutes(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 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(SystemTokenCertDbInitializerTest,
GetSystemTokenCertDbLateRequestSuccess) {
// Simulate waiting for 6 minutes after the initialization of the
// SystemTokenCertDbInitializer.
const auto kSixMinuteDelay = base::TimeDelta::FromMinutes(6);
EXPECT_GT(kSixMinuteDelay,
SystemTokenCertDBInitializer::kMaxCertDbRetrievalDelay);
task_environment()->FastForwardBy(kSixMinuteDelay);
EXPECT_TRUE(InitializeTestSystemSlot());
GetSystemTokenCertDbCallbackWrapper get_system_token_cert_db_callback_wrapper;
system_token_cert_db_initializer()->GetSystemTokenCertDb(
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(SystemTokenCertDbInitializerTest, GetSystemTokenCertDbTimeout) {
GetSystemTokenCertDbCallbackWrapper get_system_token_cert_db_callback_wrapper;
system_token_cert_db_initializer()->GetSystemTokenCertDb(
get_system_token_cert_db_callback_wrapper.GetCallback());
EXPECT_FALSE(get_system_token_cert_db_callback_wrapper.IsCallbackCalled());
const auto kDelay1 = base::TimeDelta::FromMinutes(2);
EXPECT_LT(kDelay1, SystemTokenCertDBInitializer::kMaxCertDbRetrievalDelay);
const auto kDelay2 =
SystemTokenCertDBInitializer::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(SystemTokenCertDbInitializerTest,
GetSystemTokenCertDbTimeoutMultipleRequests) {
GetSystemTokenCertDbCallbackWrapper
get_system_token_cert_db_callback_wrapper_1;
system_token_cert_db_initializer()->GetSystemTokenCertDb(
get_system_token_cert_db_callback_wrapper_1.GetCallback());
EXPECT_FALSE(get_system_token_cert_db_callback_wrapper_1.IsCallbackCalled());
task_environment()->FastForwardBy(
SystemTokenCertDBInitializer::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;
system_token_cert_db_initializer()->GetSystemTokenCertDb(
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 chromeos
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment