| // 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 |