blob: f41e3bc81a6281ea76e823923d8b6d1d19599376 [file] [log] [blame]
// Copyright 2022 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/component_updater/pki_metadata_component_installer.h"
#include <string>
#include <vector>
#include "base/base64.h"
#include "base/containers/to_vector.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "base/version.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/net/key_pinning.pb.h"
#include "components/certificate_transparency/certificate_transparency_config.pb.h"
#include "components/certificate_transparency/ct_known_logs.h"
#include "components/component_updater/component_installer.h"
#include "components/component_updater/mock_component_updater_service.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/test/browser_task_environment.h"
#include "net/base/features.h"
#include "net/base/hash_value.h"
#include "net/cert/cert_verify_proc.h"
#include "net/http/transport_security_state.h"
#include "net/net_buildflags.h"
#include "services/cert_verifier/cert_verifier_service_factory.h"
#include "services/network/network_service.h"
#include "services/network/public/cpp/network_service_buildflags.h"
#include "services/network/public/mojom/ct_log_info.mojom.h"
#include "services/network/sct_auditing/sct_auditing_cache.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
namespace component_updater {
namespace {
// An arbitrary, DER-encoded subjectpublickeyinfo encoded as BASE64.
const char kLogSPKIBase64[] =
"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1+5tMt7sGC0MTw/AloiaTsFbEpW3s"
"g3GCAFY6wRP5Izt+mV/Q9xHb450LppfptaYh94nIkVIhtTkSQ4b2GRxOAcaSkpTsN+PUPaO2D"
"Jd/5M7MFxXeHGYqXIbdD+rgSsU5rcBspFbJkRv+Q34bqNeiwKT+zcYqfAEH3cYvDGF+FIxXrZ"
"YqUAmTQJZtzHfBP/2ZWfkyAHwPJqnz3aWS0MmTLY3JUF/BnSt381U6ZtI+qvmV+aWoDg2X2kx"
"lSR2WsEGPYrO+/7dHmNn3XLqWj+aWe7qoK3wRcxsq081ONsMGKGwm6yY35I+prob1WA4avtli"
"gMjNT0t7SJZoTkHSvgtDuo8Kfb5HrSyoMwIpIyI85XBoL2zM+BTJcCP9gyUnkiq6H9bWv1S20"
"ujMYhssH+UkAvpgSYBbgq8g1ta5cpOb2sm7qqEG7V7Aw1AhXkVAuuuzjQK29feSaZOtrOueo2"
"6tGd154DY2+AzwcrBDSfNw7XZqrFpgl9saW22L+P683b0wV2rlkFmc5O4LlIyk+oCuNoubS/t"
"Iyrq1REXsoc/O0HVCXQXKP1/g6mduco4wA57lH1BSJrSet5Rc8NyR5g7zR8FPzXvav+eErLwd"
"RsVdo4HNxlBlrc50CqkbsNFg2hdU1uCbbzRHKAF5Ih/NGdFkQZ9N+pPbTcpA8z5mWyjo6cCAw"
"EAAQ==";
const char kLogIdBase64[] = "KHWaS8pa+aGJCk5BUfi+NfHcNTRSlVLLQ8/A3d3QN3w=";
constexpr uint64_t kLogMMDSeconds = 42;
const char kLogURL[] = "https://futuregadgetlab.jp";
const char kLogName[] = "FutureGadgetLog2022";
const char kLogOperatorName[] = "Future Gadget Lab";
const char kLogOperatorEmail[] = "kurisu@dmail.com";
constexpr base::TimeDelta kCurrentOperatorStart = base::Days(3);
const char kPreviousOperator1Name[] = "SERN";
constexpr base::TimeDelta kPreviousOperator1Start = base::Days(2);
const char kPreviousOperator2Name[] = "DURPA";
constexpr base::TimeDelta kPreviousOperator2Start = base::Days(1);
const char kGoogleLogName[] = "GoogleLog2022";
const char kGoogleLogOperatorName[] = "Google";
constexpr base::TimeDelta kGoogleLogDisqualificationDate = base::Days(2);
// BASE64 encoded fake leaf hashes.
const char kPopularSCT1[] = "EBESExQVFhcYGRobHB0eHwEjRWeJq83v";
const char kPopularSCT2[] = "oKGio6SlpqeoqaqrrK2urwEjRWeJq83v";
// Constants for test pinset.
const char kPinsetName[] = "example";
const char kPinsetHostName[] = "example.test";
const bool kPinsetIncludeSubdomains = true;
// SHA256 SPKI hashes.
constexpr uint8_t kSpkiHash1[] = {
0xec, 0x72, 0x29, 0x69, 0xcb, 0x64, 0x20, 0x0a, 0xb6, 0x63, 0x8f,
0x68, 0xac, 0x53, 0x8e, 0x40, 0xab, 0xab, 0x5b, 0x19, 0xa6, 0x48,
0x56, 0x61, 0x04, 0x2a, 0x10, 0x61, 0xc4, 0x61, 0x27, 0x76};
constexpr uint8_t kSpkiHash2[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
constexpr uint64_t kMaxSupportedCTCompatibilityVersion = 3;
constexpr uint64_t kMaxSupportedKPCompatibilityVersion = 1;
} // namespace
class PKIMetadataComponentInstallerTest : public testing::Test {
public:
void SetUp() override {
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/{features::
kCertificateTransparencyAskBeforeEnabling},
/*disabled_features=*/{});
ct_config_.set_disable_ct_enforcement(false);
ct_config_.mutable_log_list()->set_compatibility_version(
kMaxSupportedCTCompatibilityVersion);
ct_config_.mutable_log_list()->mutable_timestamp()->set_seconds(
(certificate_transparency::GetLogListTimestamp()
.InMillisecondsSinceUnixEpoch() /
1000) +
1);
{
auto* log_operator = ct_config_.mutable_log_list()->add_operators();
log_operator->add_email(kLogOperatorEmail);
log_operator->set_name(kLogOperatorName);
}
{
auto* log_operator = ct_config_.mutable_log_list()->add_operators();
log_operator->add_email(kLogOperatorEmail);
log_operator->set_name(kGoogleLogOperatorName);
}
{
// Configure a qualified non-google log with an operator history.
auto* ct_log = ct_config_.mutable_log_list()->add_logs();
ct_log->set_description(kLogName);
ct_log->set_key(kLogSPKIBase64);
ct_log->set_log_id(kLogIdBase64);
ct_log->set_mmd_secs(kLogMMDSeconds);
ct_log->set_url(kLogURL);
auto* operator_change = ct_log->add_operator_history();
operator_change->set_name(kLogOperatorName);
operator_change->mutable_operator_start()->set_seconds(
kCurrentOperatorStart.InSeconds());
operator_change = ct_log->add_operator_history();
operator_change->set_name(kPreviousOperator1Name);
operator_change->mutable_operator_start()->set_seconds(
kPreviousOperator1Start.InSeconds());
operator_change = ct_log->add_operator_history();
operator_change->set_name(kPreviousOperator2Name);
operator_change->mutable_operator_start()->set_seconds(
kPreviousOperator2Start.InSeconds());
auto* log_state = ct_log->add_state();
log_state->set_current_state(chrome_browser_certificate_transparency::
CTLog_CurrentState_QUALIFIED);
log_state->mutable_state_start()->set_seconds(10);
}
{
// Configure a non-qualified google log without an operator history.
auto* ct_log = ct_config_.mutable_log_list()->add_logs();
ct_log->set_description(kGoogleLogName);
ct_log->set_key(kLogSPKIBase64);
ct_log->set_log_id(kLogIdBase64);
ct_log->set_mmd_secs(kLogMMDSeconds);
ct_log->set_url(kLogURL);
auto* operator_change = ct_log->add_operator_history();
operator_change->set_name(kGoogleLogOperatorName);
operator_change->mutable_operator_start()->set_seconds(
kCurrentOperatorStart.InSeconds());
auto* log_state = ct_log->add_state();
log_state->set_current_state(
chrome_browser_certificate_transparency::CTLog_CurrentState_RETIRED);
log_state->mutable_state_start()->set_seconds(
kGoogleLogDisqualificationDate.InSeconds());
log_state = ct_log->add_state();
log_state->set_current_state(chrome_browser_certificate_transparency::
CTLog_CurrentState_QUALIFIED);
log_state->mutable_state_start()->set_seconds(10);
}
{
// Configure a log with an invalid log id.
auto* ct_log = ct_config_.mutable_log_list()->add_logs();
ct_log->set_log_id("not base64");
ct_log->set_key(kLogSPKIBase64);
}
{
// Configure a log with an invalid log key.
auto* ct_log = ct_config_.mutable_log_list()->add_logs();
ct_log->set_log_id(kLogIdBase64);
ct_log->set_key("not base64");
}
// Configure some popular SCTs.
ASSERT_TRUE(
base::Base64Decode(kPopularSCT1, ct_config_.add_popular_scts()));
ASSERT_TRUE(
base::Base64Decode(kPopularSCT2, ct_config_.add_popular_scts()));
// Configure the key pinning pins list.
{
pinlist_.mutable_timestamp()->set_seconds(
(base::Time::Now() - base::Time::UnixEpoch()).InSeconds());
pinlist_.set_compatibility_version(kMaxSupportedKPCompatibilityVersion);
auto* host_pin = pinlist_.add_host_pins();
host_pin->set_hostname(kPinsetHostName);
host_pin->set_pinset_name(kPinsetName);
host_pin->set_include_subdomains(kPinsetIncludeSubdomains);
auto* pinset = pinlist_.add_pinsets();
pinset->set_name(kPinsetName);
std::string spki_bytes_as_string(std::begin(kSpkiHash1),
std::end(kSpkiHash1));
std::string bad_spki_bytes_as_string(std::begin(kSpkiHash2),
std::end(kSpkiHash2));
pinset->add_static_spki_hashes_sha256(spki_bytes_as_string);
pinset->add_bad_static_spki_hashes_sha256(bad_spki_bytes_as_string);
}
}
void WriteCTConfigToFile() {
ASSERT_TRUE(component_install_dir_.CreateUniqueTempDir());
base::FilePath ct_file_path = component_install_dir_.GetPath().Append(
FILE_PATH_LITERAL("ct_config.pb"));
std::string ct_data;
ASSERT_TRUE(ct_config_.SerializeToString(&ct_data));
ASSERT_TRUE(base::WriteFile(ct_file_path, ct_data));
}
void WriteKPConfigToFile() {
ASSERT_TRUE(component_install_dir_.CreateUniqueTempDir());
base::FilePath kp_file_path = component_install_dir_.GetPath().Append(
FILE_PATH_LITERAL("kp_pinslist.pb"));
std::string kp_data;
ASSERT_TRUE(pinlist_.SerializeToString(&kp_data));
ASSERT_TRUE(base::WriteFile(kp_file_path, kp_data));
}
protected:
base::test::ScopedFeatureList scoped_feature_list_;
content::BrowserTaskEnvironment task_environment_;
component_updater::MockComponentUpdateService mock_component_update_;
base::ScopedTempDir component_install_dir_;
chrome_browser_certificate_transparency::CTConfig ct_config_;
chrome_browser_key_pinning::PinList pinlist_;
std::unique_ptr<component_updater::ComponentInstallerPolicy> policy_ =
std::make_unique<
component_updater::PKIMetadataComponentInstallerPolicy>();
};
TEST_F(PKIMetadataComponentInstallerTest, TestProtoBytesConversion) {
std::vector<net::SHA256HashValue> test_hashes = {
{0xec, 0x72, 0x29, 0x69, 0xcb, 0x64, 0x20, 0x0a, 0xb6, 0x63, 0x8f,
0x68, 0xac, 0x53, 0x8e, 0x40, 0xab, 0xab, 0x5b, 0x19, 0xa6, 0x48,
0x56, 0x61, 0x04, 0x2a, 0x10, 0x61, 0xc4, 0x61, 0x27, 0x76}};
std::vector<std::vector<uint8_t>> test_bytes = {
base::ToVector(test_hashes[0])};
std::string bytes_as_string(std::begin(test_bytes[0]),
std::end(test_bytes[0]));
std::vector<std::string> repeated_bytes = {bytes_as_string};
EXPECT_EQ(
PKIMetadataComponentInstallerPolicy::BytesArrayFromProtoBytesForTesting(
google::protobuf::RepeatedPtrField<std::string>(
repeated_bytes.begin(), repeated_bytes.end())),
test_bytes);
EXPECT_EQ(PKIMetadataComponentInstallerPolicy::
SHA256HashValueArrayFromProtoBytesForTesting(
google::protobuf::RepeatedPtrField<std::string>(
repeated_bytes.begin(), repeated_bytes.end())),
test_hashes);
}
// Tests that the installation is verified iff the component install directory
// exists.
TEST_F(PKIMetadataComponentInstallerTest, VerifyInstallation) {
WriteCTConfigToFile();
base::FilePath path = component_install_dir_.GetPath();
EXPECT_TRUE(policy_->VerifyInstallation(base::Value::Dict(), path));
ASSERT_TRUE(component_install_dir_.Delete());
EXPECT_FALSE(policy_->VerifyInstallation(base::Value::Dict(), path));
WriteKPConfigToFile();
path = component_install_dir_.GetPath();
EXPECT_TRUE(policy_->VerifyInstallation(base::Value::Dict(), path));
ASSERT_TRUE(component_install_dir_.Delete());
EXPECT_FALSE(policy_->VerifyInstallation(base::Value::Dict(), path));
}
// Tests that the PKI Metadata component is registered if the features are
// enabled.
TEST_F(PKIMetadataComponentInstallerTest, RegisterComponent) {
EXPECT_CALL(mock_component_update_, RegisterComponent)
.WillOnce(testing::Return(true));
component_updater::MaybeRegisterPKIMetadataComponent(&mock_component_update_);
task_environment_.RunUntilIdle();
}
// Tests that setting the CT enforcement kill switch successfully disables CT
// enforcement.
TEST_F(PKIMetadataComponentInstallerTest, CTEnforcementKillSwitch) {
// Initialize the network service.
content::GetNetworkService();
task_environment_.RunUntilIdle();
ct_config_.set_disable_ct_enforcement(true);
WriteCTConfigToFile();
policy_->ComponentReady(base::Version("1.2.3.4"),
component_install_dir_.GetPath(),
base::Value::Dict());
task_environment_.RunUntilIdle();
network::NetworkService* network_service =
network::NetworkService::GetNetworkServiceForTesting();
ASSERT_TRUE(network_service);
// Logs should not have been updated.
const std::vector<network::mojom::CTLogInfoPtr>& logs =
network_service->log_list();
EXPECT_EQ(logs.size(), 0u);
EXPECT_FALSE(network_service->is_ct_enforcement_enabled_for_testing());
}
// Tests that installing the component updates the key pinning configuration in
// the network service.
TEST_F(PKIMetadataComponentInstallerTest,
InstallComponentUpdatesPinningConfig) {
// Initialize the network service.
content::GetNetworkService();
task_environment_.RunUntilIdle();
WriteKPConfigToFile();
policy_->ComponentReady(base::Version("1.2.3.4"),
component_install_dir_.GetPath(),
base::Value::Dict());
task_environment_.RunUntilIdle();
network::NetworkService* network_service =
network::NetworkService::GetNetworkServiceForTesting();
ASSERT_TRUE(network_service);
EXPECT_TRUE(network_service->pins_list_updated());
const std::vector<net::TransportSecurityState::PinSet>& pinsets =
network_service->pinsets();
const std::vector<net::TransportSecurityState::PinSetInfo>& host_pins =
network_service->host_pins();
EXPECT_EQ(pinsets.size(), 1u);
EXPECT_EQ(host_pins.size(), 1u);
EXPECT_EQ(pinsets.at(0).name(), kPinsetName);
EXPECT_THAT(pinsets.at(0).static_spki_hashes().at(0),
::testing::ElementsAreArray(kSpkiHash1));
EXPECT_THAT(pinsets.at(0).bad_static_spki_hashes().at(0),
::testing::ElementsAreArray(kSpkiHash2));
EXPECT_EQ(host_pins.at(0).hostname_, kPinsetHostName);
EXPECT_EQ(host_pins.at(0).pinset_name_, kPinsetName);
EXPECT_EQ(host_pins.at(0).include_subdomains_, kPinsetIncludeSubdomains);
}
// Tests that installing the PKI Metadata component bails out if the KP proto is
// invalid.
TEST_F(PKIMetadataComponentInstallerTest, InstallComponentInvalidKPProto) {
// Initialize the network service.
content::GetNetworkService();
task_environment_.RunUntilIdle();
// Write an invalid kp_pinslist.pb.
ASSERT_TRUE(component_install_dir_.CreateUniqueTempDir());
base::FilePath file_path = component_install_dir_.GetPath().Append(
FILE_PATH_LITERAL("kp_pinslist.pb"));
ASSERT_TRUE(base::WriteFile(file_path, "mismatch"));
policy_->ComponentReady(base::Version("1.2.3.4"),
component_install_dir_.GetPath(),
base::Value::Dict());
task_environment_.RunUntilIdle();
network::NetworkService* network_service =
network::NetworkService::GetNetworkServiceForTesting();
ASSERT_TRUE(network_service);
// The pins list should not have been updated.
EXPECT_FALSE(network_service->pins_list_updated());
const std::vector<net::TransportSecurityState::PinSet>& pinsets =
network_service->pinsets();
const std::vector<net::TransportSecurityState::PinSetInfo>& host_pins =
network_service->host_pins();
EXPECT_EQ(pinsets.size(), 0u);
EXPECT_EQ(host_pins.size(), 0u);
}
// Tests that installing the PKI Metadata component does not update the pinning
// list if its compatibility version exceeds the value supported.
TEST_F(PKIMetadataComponentInstallerTest,
InstallComponentIncompatibleKPVersion) {
// Initialize the network service.
content::GetNetworkService();
task_environment_.RunUntilIdle();
// Change the version to an unsupported value.
pinlist_.set_compatibility_version(kMaxSupportedKPCompatibilityVersion + 1);
WriteKPConfigToFile();
policy_->ComponentReady(base::Version("1.2.3.4"),
component_install_dir_.GetPath(),
base::Value::Dict());
task_environment_.RunUntilIdle();
network::NetworkService* network_service =
network::NetworkService::GetNetworkServiceForTesting();
ASSERT_TRUE(network_service);
// The pin list should not have been updated.
EXPECT_FALSE(network_service->pins_list_updated());
const std::vector<net::TransportSecurityState::PinSet>& pinsets =
network_service->pinsets();
const std::vector<net::TransportSecurityState::PinSetInfo>& host_pins =
network_service->host_pins();
EXPECT_EQ(pinsets.size(), 0u);
EXPECT_EQ(host_pins.size(), 0u);
}
#if BUILDFLAG(INCLUDE_TRANSPORT_SECURITY_STATE_PRELOAD_LIST)
// Tests that installing the PKI Metadata component does not update the pinning
// list if the built in list is newer.
TEST_F(PKIMetadataComponentInstallerTest,
InstallComponentKPListOlderThanBuiltIn) {
// Initialize the network service.
content::GetNetworkService();
task_environment_.RunUntilIdle();
// Change the timestamp so it's older than the built in list.
pinlist_.mutable_timestamp()->set_seconds(
(net::TransportSecurityState::GetBuiltInPinsListTimestamp()
.InMillisecondsSinceUnixEpoch() /
1000) -
1);
WriteKPConfigToFile();
policy_->ComponentReady(base::Version("1.2.3.4"),
component_install_dir_.GetPath(),
base::Value::Dict());
task_environment_.RunUntilIdle();
network::NetworkService* network_service =
network::NetworkService::GetNetworkServiceForTesting();
ASSERT_TRUE(network_service);
// The pin list should not have been updated.
EXPECT_FALSE(network_service->pins_list_updated());
EXPECT_EQ(network_service->pinsets().size(), 0u);
EXPECT_EQ(network_service->host_pins().size(), 0u);
}
#endif
#if BUILDFLAG(IS_CT_SUPPORTED)
// Tests that installing the PKI Metadata component updates the CT configuration
// in the network service.
TEST_F(PKIMetadataComponentInstallerTest, InstallComponentUpdatesCTConfig) {
// Initialize the network service.
content::GetNetworkService();
task_environment_.RunUntilIdle();
WriteCTConfigToFile();
policy_->ComponentReady(base::Version("1.2.3.4"),
component_install_dir_.GetPath(),
base::Value::Dict());
task_environment_.RunUntilIdle();
network::NetworkService* network_service =
network::NetworkService::GetNetworkServiceForTesting();
ASSERT_TRUE(network_service);
const std::vector<network::mojom::CTLogInfoPtr>& logs =
network_service->log_list();
ASSERT_EQ(logs.size(), 2u);
std::string expected_log_id;
ASSERT_TRUE(base::Base64Decode(kLogIdBase64, &expected_log_id));
EXPECT_EQ(logs.at(0)->id, expected_log_id);
std::string expected_public_key;
ASSERT_TRUE(base::Base64Decode(kLogSPKIBase64, &expected_public_key));
EXPECT_EQ(logs.at(0)->public_key, expected_public_key);
EXPECT_EQ(logs.at(0)->name, kLogName);
EXPECT_FALSE(logs.at(0)->disqualified_at);
EXPECT_EQ(logs.at(0)->mmd, base::Seconds(kLogMMDSeconds));
EXPECT_EQ(logs.at(0)->current_operator, kLogOperatorName);
EXPECT_EQ(logs.at(1)->id, expected_log_id);
EXPECT_EQ(logs.at(1)->public_key, expected_public_key);
EXPECT_EQ(logs.at(1)->name, kGoogleLogName);
EXPECT_EQ(*logs.at(1)->disqualified_at,
base::Time::UnixEpoch() + kGoogleLogDisqualificationDate);
EXPECT_EQ(logs.at(1)->mmd, base::Seconds(kLogMMDSeconds));
EXPECT_EQ(logs.at(1)->current_operator, kGoogleLogOperatorName);
// Previous operators should be sorted in the opposite order as in the proto.
std::vector<network::mojom::PreviousOperatorEntryPtr>& previous_operators =
logs.at(0)->previous_operators;
ASSERT_EQ(previous_operators.size(), 2u);
EXPECT_EQ(previous_operators.at(0)->name, kPreviousOperator2Name);
EXPECT_EQ(previous_operators.at(0)->end_time,
base::Time::UnixEpoch() + kPreviousOperator1Start);
EXPECT_EQ(previous_operators.at(1)->name, kPreviousOperator1Name);
EXPECT_EQ(previous_operators.at(1)->end_time,
base::Time::UnixEpoch() + kCurrentOperatorStart);
network::SCTAuditingCache* cache = network_service->sct_auditing_cache();
EXPECT_TRUE(cache->IsPopularSCT(*base::Base64Decode(kPopularSCT1)));
EXPECT_TRUE(cache->IsPopularSCT(*base::Base64Decode(kPopularSCT2)));
EXPECT_FALSE(cache->IsPopularSCT(std::vector<uint8_t>{1, 2, 3, 4}));
EXPECT_TRUE(network_service->is_ct_enforcement_enabled_for_testing());
cert_verifier::CertVerifierServiceFactoryImpl* cert_verifier_service_factory =
content::GetCertVerifierServiceFactoryForTesting();
ASSERT_TRUE(cert_verifier_service_factory);
const net::CertVerifyProc::ImplParams& impl_params =
cert_verifier_service_factory->get_impl_params();
ASSERT_EQ(impl_params.ct_logs.size(), 2u);
EXPECT_EQ(impl_params.ct_logs[0]->key_id(), expected_log_id);
EXPECT_EQ(impl_params.ct_logs[0]->description(), kLogName);
EXPECT_EQ(impl_params.ct_logs[1]->key_id(), expected_log_id);
EXPECT_EQ(impl_params.ct_logs[1]->description(), kGoogleLogName);
}
// Tests that installing the PKI Metadata component bails out if the CT proto is
// invalid.
TEST_F(PKIMetadataComponentInstallerTest, InstallComponentInvalidCTProto) {
// Initialize the network service and cert verifier service factory.
content::GetNetworkService();
content::GetCertVerifierServiceFactory();
// Write an invalid ct_config.pb.
ASSERT_TRUE(component_install_dir_.CreateUniqueTempDir());
base::FilePath file_path = component_install_dir_.GetPath().Append(
FILE_PATH_LITERAL("ct_config.pb"));
ASSERT_TRUE(base::WriteFile(file_path, "mismatch"));
policy_->ComponentReady(base::Version("1.2.3.4"),
component_install_dir_.GetPath(),
base::Value::Dict());
task_environment_.RunUntilIdle();
network::NetworkService* network_service =
network::NetworkService::GetNetworkServiceForTesting();
ASSERT_TRUE(network_service);
// The logs should not have been updated.
const std::vector<network::mojom::CTLogInfoPtr>& logs =
network_service->log_list();
EXPECT_EQ(logs.size(), 0u);
EXPECT_TRUE(network_service->is_ct_enforcement_enabled_for_testing());
cert_verifier::CertVerifierServiceFactoryImpl* cert_verifier_service_factory =
content::GetCertVerifierServiceFactoryForTesting();
ASSERT_TRUE(cert_verifier_service_factory);
const net::CertVerifyProc::ImplParams& impl_params =
cert_verifier_service_factory->get_impl_params();
EXPECT_EQ(impl_params.ct_logs.size(), 0u);
}
// Tests that installing the PKI Metadata component does not update the CT log
// list if its compatibility version exceeds the value supported.
TEST_F(PKIMetadataComponentInstallerTest,
InstallComponentIncompatibleCTVersion) {
// Initialize the network service and cert verifier service factory.
content::GetNetworkService();
content::GetCertVerifierServiceFactory();
task_environment_.RunUntilIdle();
// Change the version to an unsupported values.
ct_config_.mutable_log_list()->set_compatibility_version(
kMaxSupportedCTCompatibilityVersion + 1);
WriteCTConfigToFile();
policy_->ComponentReady(base::Version("1.2.3.4"),
component_install_dir_.GetPath(),
base::Value::Dict());
task_environment_.RunUntilIdle();
network::NetworkService* network_service =
network::NetworkService::GetNetworkServiceForTesting();
ASSERT_TRUE(network_service);
// The logs should not have been updated.
const std::vector<network::mojom::CTLogInfoPtr>& logs =
network_service->log_list();
EXPECT_EQ(logs.size(), 0u);
EXPECT_TRUE(network_service->is_ct_enforcement_enabled_for_testing());
cert_verifier::CertVerifierServiceFactoryImpl* cert_verifier_service_factory =
content::GetCertVerifierServiceFactoryForTesting();
ASSERT_TRUE(cert_verifier_service_factory);
const net::CertVerifyProc::ImplParams& impl_params =
cert_verifier_service_factory->get_impl_params();
EXPECT_EQ(impl_params.ct_logs.size(), 0u);
}
// Tests that installing the PKI Metadata component does not update the CT log
// list if the log list it includes is older than the built in one.
TEST_F(PKIMetadataComponentInstallerTest,
InstallComponentCTListOlderThanBuiltIn) {
// Initialize the network service and cert verifier service factory.
content::GetNetworkService();
content::GetCertVerifierServiceFactory();
task_environment_.RunUntilIdle();
// Change the timestamp so it is older than the built in list.
ct_config_.mutable_log_list()->mutable_timestamp()->set_seconds(
(certificate_transparency::GetLogListTimestamp()
.InMillisecondsSinceUnixEpoch() /
1000) -
1);
WriteCTConfigToFile();
policy_->ComponentReady(base::Version("1.2.3.4"),
component_install_dir_.GetPath(),
base::Value::Dict());
task_environment_.RunUntilIdle();
network::NetworkService* network_service =
network::NetworkService::GetNetworkServiceForTesting();
ASSERT_TRUE(network_service);
// The logs should not have been updated.
EXPECT_EQ(network_service->log_list().size(), 0u);
EXPECT_TRUE(network_service->is_ct_enforcement_enabled_for_testing());
cert_verifier::CertVerifierServiceFactoryImpl* cert_verifier_service_factory =
content::GetCertVerifierServiceFactoryForTesting();
ASSERT_TRUE(cert_verifier_service_factory);
EXPECT_EQ(cert_verifier_service_factory->get_impl_params().ct_logs.size(),
0u);
}
// Tests that calling |ReconfigureAfterNetworkRestart| is a no-op if the
// component has not been installed.
TEST_F(PKIMetadataComponentInstallerTest, ReconfigureWhenNotInstalled) {
// Initialize the network service and cert verifier service factory.
content::GetNetworkService();
content::GetCertVerifierServiceFactory();
task_environment_.RunUntilIdle();
PKIMetadataComponentInstallerService::GetInstance()
->ReconfigureAfterNetworkRestart();
// The logs should not have been updated.
network::NetworkService* network_service =
network::NetworkService::GetNetworkServiceForTesting();
ASSERT_TRUE(network_service);
const std::vector<network::mojom::CTLogInfoPtr>& logs =
network_service->log_list();
EXPECT_EQ(logs.size(), 0u);
cert_verifier::CertVerifierServiceFactoryImpl* cert_verifier_service_factory =
content::GetCertVerifierServiceFactoryForTesting();
ASSERT_TRUE(cert_verifier_service_factory);
const net::CertVerifyProc::ImplParams& impl_params =
cert_verifier_service_factory->get_impl_params();
EXPECT_EQ(impl_params.ct_logs.size(), 0u);
}
#endif // BUILDFLAG(IS_CT_SUPPORTED)
class PKIMetadataComponentInstallerDisabledTest
: public PKIMetadataComponentInstallerTest {
void SetUp() override {
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/{},
/*disabled_features=*/{
features::kCertificateTransparencyAskBeforeEnabling});
}
};
// Tests that the PKI Metadata component does not get registered if both the CT
// component updater and KP component updater features are disabled.
TEST_F(PKIMetadataComponentInstallerDisabledTest,
MaybeDoNotRegisterIfFeatureDisabled) {
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
// If Chrome Root Store is supported on this build config, PKI metadata
// component will always be registered even if the other feature flags are
// disabled.
EXPECT_CALL(mock_component_update_, RegisterComponent)
.WillOnce(testing::Return(true));
#else
EXPECT_CALL(mock_component_update_, RegisterComponent).Times(0);
#endif
component_updater::MaybeRegisterPKIMetadataComponent(&mock_component_update_);
task_environment_.RunUntilIdle();
}
} // namespace component_updater