Add component installer for certificate transparency
Adds the component installer for the new CT component. This plumbs the
component delivered log list to the network service and
MultiLogCTVerifier (which uses it to check compliance).
Not addressed yet on this CL:
-Plumbing the log list to ChromeCTPolicyEnforcer.
-Implementing the emergency disable switch.
-Using the log list timestamp instead of build date.
Bug: 1199878
Change-Id: Id413ea2b28b82f063e9a5e6576ec021d5f7e14d3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2831544
Commit-Queue: Carlos IL <carlosil@chromium.org>
Reviewed-by: Mustafa Emre Acer <meacer@chromium.org>
Reviewed-by: Eric Orth <ericorth@chromium.org>
Reviewed-by: Ryan Sleevi <rsleevi@chromium.org>
Reviewed-by: Joshua Pawlicki <waffles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#886492}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
a07549c388
commit
d0ba805fc9
chrome/browser
components/certificate_transparency
BUILD.gncertificate_transparency.protocertificate_transparency_config.protoct_features.ccct_features.hct_timestamp.proto
net
cert
cert_and_ct_verifier.hmulti_log_ct_verifier.ccmulti_log_ct_verifier.hmulti_log_ct_verifier_unittest.cc
quic
services/network
@ -329,6 +329,8 @@ static_library("browser") {
|
||||
"component_updater/mei_preload_component_installer.h",
|
||||
"component_updater/pepper_flash_component_installer.cc",
|
||||
"component_updater/pepper_flash_component_installer.h",
|
||||
"component_updater/pki_metadata_component_installer.cc",
|
||||
"component_updater/pki_metadata_component_installer.h",
|
||||
"component_updater/pnacl_component_installer.cc",
|
||||
"component_updater/pnacl_component_installer.h",
|
||||
"component_updater/recovery_component_installer.cc",
|
||||
@ -2425,6 +2427,7 @@ static_library("browser") {
|
||||
"//chromeos/timezone",
|
||||
"//chromeos/tpm",
|
||||
"//components/arc/mojom",
|
||||
"//components/certificate_transparency",
|
||||
"//components/drive",
|
||||
"//components/quirks",
|
||||
"//components/session_manager/core",
|
||||
|
@ -0,0 +1,215 @@
|
||||
// Copyright 2021 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/component_updater/pki_metadata_component_installer.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/base64.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/feature_list.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
#include "base/time/time.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/network_service_instance.h"
|
||||
#include "services/network/public/cpp/network_service_buildflags.h"
|
||||
|
||||
#if BUILDFLAG(IS_CT_SUPPORTED)
|
||||
#include "components/certificate_transparency/certificate_transparency.pb.h"
|
||||
#include "components/certificate_transparency/certificate_transparency_config.pb.h"
|
||||
#include "components/certificate_transparency/ct_features.h"
|
||||
#include "services/network/public/mojom/ct_log_info.mojom.h"
|
||||
#include "services/network/public/mojom/network_service.mojom.h"
|
||||
#endif
|
||||
|
||||
using component_updater::ComponentUpdateService;
|
||||
|
||||
namespace {
|
||||
|
||||
const char kGoogleOperatorName[] = "Google";
|
||||
|
||||
// The SHA256 of the SubjectPublicKeyInfo used to sign the extension.
|
||||
// The extension id is: efniojlnjndmcbiieegkicadnoecjjef
|
||||
const uint8_t kPKIMetadataPublicKeySHA256[32] = {
|
||||
0x45, 0xd8, 0xe9, 0xbd, 0x9d, 0x3c, 0x21, 0x88, 0x44, 0x6a, 0x82,
|
||||
0x03, 0xde, 0x42, 0x99, 0x45, 0x66, 0x25, 0xfe, 0xb3, 0xd1, 0xf8,
|
||||
0x11, 0x65, 0xb4, 0x6f, 0xd3, 0x1b, 0x21, 0x89, 0xbe, 0x9c};
|
||||
|
||||
const base::FilePath::CharType kCTConfigProtoFileName[] =
|
||||
FILE_PATH_LITERAL("ct_config.pb");
|
||||
|
||||
std::string LoadCTBinaryProtoFromDisk(const base::FilePath& pb_path) {
|
||||
std::string result;
|
||||
if (pb_path.empty())
|
||||
return result;
|
||||
|
||||
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
|
||||
base::BlockingType::WILL_BLOCK);
|
||||
|
||||
if (!base::ReadFileToString(pb_path.Append(kCTConfigProtoFileName),
|
||||
&result)) {
|
||||
result.clear();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace component_updater {
|
||||
|
||||
PKIMetadataComponentInstallerPolicy::PKIMetadataComponentInstallerPolicy() =
|
||||
default;
|
||||
|
||||
PKIMetadataComponentInstallerPolicy::~PKIMetadataComponentInstallerPolicy() =
|
||||
default;
|
||||
|
||||
bool PKIMetadataComponentInstallerPolicy::
|
||||
SupportsGroupPolicyEnabledComponentUpdates() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PKIMetadataComponentInstallerPolicy::RequiresNetworkEncryption() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
update_client::CrxInstaller::Result
|
||||
PKIMetadataComponentInstallerPolicy::OnCustomInstall(
|
||||
const base::DictionaryValue& /* manifest */,
|
||||
const base::FilePath& /* install_dir */) {
|
||||
return update_client::CrxInstaller::Result(0); // Nothing custom here.
|
||||
}
|
||||
|
||||
void PKIMetadataComponentInstallerPolicy::OnCustomUninstall() {}
|
||||
|
||||
void PKIMetadataComponentInstallerPolicy::ComponentReady(
|
||||
const base::Version& version,
|
||||
const base::FilePath& install_dir,
|
||||
std::unique_ptr<base::DictionaryValue> /* manifest */) {
|
||||
base::ThreadPool::PostTaskAndReplyWithResult(
|
||||
FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
|
||||
base::BindOnce(&LoadCTBinaryProtoFromDisk, install_dir),
|
||||
base::BindOnce(
|
||||
&PKIMetadataComponentInstallerPolicy::UpdateNetworkServiceOnUI,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
// Called during startup and installation before ComponentReady().
|
||||
bool PKIMetadataComponentInstallerPolicy::VerifyInstallation(
|
||||
const base::DictionaryValue& /* manifest */,
|
||||
const base::FilePath& install_dir) const {
|
||||
if (!base::PathExists(install_dir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
base::FilePath PKIMetadataComponentInstallerPolicy::GetRelativeInstallDir()
|
||||
const {
|
||||
return base::FilePath(FILE_PATH_LITERAL("PKIMetadata"));
|
||||
}
|
||||
|
||||
void PKIMetadataComponentInstallerPolicy::GetHash(
|
||||
std::vector<uint8_t>* hash) const {
|
||||
hash->assign(std::begin(kPKIMetadataPublicKeySHA256),
|
||||
std::end(kPKIMetadataPublicKeySHA256));
|
||||
}
|
||||
|
||||
std::string PKIMetadataComponentInstallerPolicy::GetName() const {
|
||||
return "PKI Metadata";
|
||||
}
|
||||
|
||||
update_client::InstallerAttributes
|
||||
PKIMetadataComponentInstallerPolicy::GetInstallerAttributes() const {
|
||||
return update_client::InstallerAttributes();
|
||||
}
|
||||
|
||||
void PKIMetadataComponentInstallerPolicy::UpdateNetworkServiceOnUI(
|
||||
const std::string& ct_config_bytes) {
|
||||
#if BUILDFLAG(IS_CT_SUPPORTED)
|
||||
auto proto =
|
||||
std::make_unique<chrome_browser_certificate_transparency::CTConfig>();
|
||||
if (!proto->ParseFromString(ct_config_bytes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
network::mojom::NetworkService* network_service =
|
||||
content::GetNetworkService();
|
||||
|
||||
if (proto->disable_ct_enforcement()) {
|
||||
network_service->SetCtEnforcementEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<network::mojom::CTLogInfoPtr> log_list_mojo;
|
||||
|
||||
// The log list shipped via component updater is a single message of CTLogList
|
||||
// type, as defined in
|
||||
// components/certificate_transparency/certificate_transparency.proto, the
|
||||
// included logs are of the CTLog type, but include only the information
|
||||
// required by Chrome to enforce its CT policy. Non Chrome used fields are
|
||||
// left unset.
|
||||
for (auto log : proto->log_list().logs()) {
|
||||
std::string decoded_key;
|
||||
if (!base::Base64Decode(log.key(), &decoded_key)) {
|
||||
continue;
|
||||
}
|
||||
network::mojom::CTLogInfoPtr log_ptr = network::mojom::CTLogInfo::New();
|
||||
log_ptr->name = log.description();
|
||||
log_ptr->public_key = decoded_key;
|
||||
// Operator history is ordered in inverse chronological order, so the 0th
|
||||
// element will be the current operator.
|
||||
if (!log.operator_history().empty() &&
|
||||
log.operator_history().Get(0).name() == kGoogleOperatorName) {
|
||||
log_ptr->operated_by_google = true;
|
||||
}
|
||||
// State history is ordered in inverse chronological order, so the 0th
|
||||
// element will be the current state.
|
||||
if (!log.state().empty()) {
|
||||
const auto& state = log.state().Get(0);
|
||||
if (state.current_state() ==
|
||||
chrome_browser_certificate_transparency::CTLog_CurrentState_RETIRED) {
|
||||
// If the log was RETIRED, record the timestamp at which it was.
|
||||
// Note: RETIRED is a terminal state for the log, so other states do not
|
||||
// need to be checked, because once RETIRED, the state will never
|
||||
// change.
|
||||
base::TimeDelta retired_since =
|
||||
base::TimeDelta::FromSeconds(
|
||||
log.state()[0].state_start().seconds()) +
|
||||
base::TimeDelta::FromNanoseconds(
|
||||
log.state()[0].state_start().nanos());
|
||||
log_ptr->disqualified_at = retired_since;
|
||||
}
|
||||
}
|
||||
log_list_mojo.push_back(std::move(log_ptr));
|
||||
}
|
||||
|
||||
network_service->UpdateCtLogList(std::move(log_list_mojo));
|
||||
#endif // BUILDFLAG(IS_CT_SUPPORTED)
|
||||
}
|
||||
|
||||
void MaybeRegisterPKIMetadataComponent(ComponentUpdateService* cus) {
|
||||
// Currently the component is only used for the CT log list, so we no-op if CT
|
||||
// is not supported.
|
||||
#if BUILDFLAG(IS_CT_SUPPORTED)
|
||||
if (!base::FeatureList::IsEnabled(
|
||||
certificate_transparency::features::
|
||||
kCertificateTransparencyComponentUpdater)) {
|
||||
return;
|
||||
}
|
||||
auto installer = base::MakeRefCounted<ComponentInstaller>(
|
||||
std::make_unique<PKIMetadataComponentInstallerPolicy>());
|
||||
installer->Register(cus, base::OnceClosure());
|
||||
#endif // BUILDFLAG(IS_CT_SUPPORTED)
|
||||
}
|
||||
|
||||
} // namespace component_updater
|
@ -0,0 +1,56 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#ifndef CHROME_BROWSER_COMPONENT_UPDATER_PKI_METADATA_COMPONENT_INSTALLER_H_
|
||||
#define CHROME_BROWSER_COMPONENT_UPDATER_PKI_METADATA_COMPONENT_INSTALLER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "components/component_updater/component_installer.h"
|
||||
|
||||
namespace component_updater {
|
||||
|
||||
// Component installer policy for the PKIMetadata component. This component
|
||||
// includes any dynamically updateable needed for PKI policies enforcement.
|
||||
// Initially this contains the Certificate Transparency log list.
|
||||
class PKIMetadataComponentInstallerPolicy : public ComponentInstallerPolicy {
|
||||
public:
|
||||
PKIMetadataComponentInstallerPolicy();
|
||||
PKIMetadataComponentInstallerPolicy(
|
||||
const PKIMetadataComponentInstallerPolicy&) = delete;
|
||||
PKIMetadataComponentInstallerPolicy operator=(
|
||||
const PKIMetadataComponentInstallerPolicy&) = delete;
|
||||
~PKIMetadataComponentInstallerPolicy() override;
|
||||
|
||||
private:
|
||||
// ComponentInstallerPolicy methods:
|
||||
bool SupportsGroupPolicyEnabledComponentUpdates() const override;
|
||||
bool RequiresNetworkEncryption() const override;
|
||||
update_client::CrxInstaller::Result OnCustomInstall(
|
||||
const base::DictionaryValue& manifest,
|
||||
const base::FilePath& install_dir) override;
|
||||
void OnCustomUninstall() override;
|
||||
bool VerifyInstallation(const base::DictionaryValue& manifest,
|
||||
const base::FilePath& install_dir) const override;
|
||||
void ComponentReady(const base::Version& version,
|
||||
const base::FilePath& install_dir,
|
||||
std::unique_ptr<base::DictionaryValue> manifest) override;
|
||||
base::FilePath GetRelativeInstallDir() const override;
|
||||
void GetHash(std::vector<uint8_t>* hash) const override;
|
||||
std::string GetName() const override;
|
||||
update_client::InstallerAttributes GetInstallerAttributes() const override;
|
||||
|
||||
// Updates the network service with the component delivered data.
|
||||
// |ct_config_bytes| should be a serialized CTLogList proto message.
|
||||
void UpdateNetworkServiceOnUI(const std::string& ct_config_bytes);
|
||||
};
|
||||
|
||||
void MaybeRegisterPKIMetadataComponent(ComponentUpdateService* cus);
|
||||
|
||||
} // namespace component_updater
|
||||
|
||||
#endif // CHROME_BROWSER_COMPONENT_UPDATER_PKI_METADATA_COMPONENT_INSTALLER_H_
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "chrome/browser/component_updater/registration.h"
|
||||
|
||||
#include "base/feature_list.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/metrics/histogram_functions.h"
|
||||
#include "base/path_service.h"
|
||||
@ -24,6 +25,7 @@
|
||||
#include "chrome/browser/component_updater/hyphenation_component_installer.h"
|
||||
#include "chrome/browser/component_updater/mei_preload_component_installer.h"
|
||||
#include "chrome/browser/component_updater/pepper_flash_component_installer.h"
|
||||
#include "chrome/browser/component_updater/pki_metadata_component_installer.h"
|
||||
#include "chrome/browser/component_updater/ssl_error_assistant_component_installer.h"
|
||||
#include "chrome/browser/component_updater/sth_set_component_remover.h"
|
||||
#include "chrome/browser/component_updater/subresource_filter_component_installer.h"
|
||||
@ -169,6 +171,8 @@ void RegisterComponentsForUpdate(bool is_off_the_record_profile,
|
||||
}
|
||||
#endif
|
||||
|
||||
MaybeRegisterPKIMetadataComponent(cus);
|
||||
|
||||
RegisterSafetyTipsComponent(cus);
|
||||
RegisterCrowdDenyComponent(cus);
|
||||
|
||||
|
@ -515,6 +515,37 @@ void SystemNetworkContextManager::OnNetworkServiceCreated(
|
||||
client_remote.InitWithNewPipeAndPassReceiver());
|
||||
network_service_network_context_->SetClient(std::move(client_remote));
|
||||
|
||||
// Configure the Certificate Transparency logs.
|
||||
#if !defined(OS_ANDROID)
|
||||
if (g_enable_certificate_transparency) {
|
||||
std::vector<std::string> operated_by_google_logs =
|
||||
certificate_transparency::GetLogsOperatedByGoogle();
|
||||
std::vector<std::pair<std::string, base::TimeDelta>> disqualified_logs =
|
||||
certificate_transparency::GetDisqualifiedLogs();
|
||||
std::vector<network::mojom::CTLogInfoPtr> log_list_mojo;
|
||||
for (const auto& ct_log : certificate_transparency::GetKnownLogs()) {
|
||||
network::mojom::CTLogInfoPtr log_info = network::mojom::CTLogInfo::New();
|
||||
log_info->public_key = std::string(ct_log.log_key, ct_log.log_key_length);
|
||||
log_info->name = ct_log.log_name;
|
||||
|
||||
std::string log_id = crypto::SHA256HashString(log_info->public_key);
|
||||
log_info->operated_by_google =
|
||||
std::binary_search(std::begin(operated_by_google_logs),
|
||||
std::end(operated_by_google_logs), log_id);
|
||||
auto it = std::lower_bound(
|
||||
std::begin(disqualified_logs), std::end(disqualified_logs), log_id,
|
||||
[](const auto& disqualified_log, const std::string& log_id) {
|
||||
return disqualified_log.first < log_id;
|
||||
});
|
||||
if (it != std::end(disqualified_logs) && it->first == log_id) {
|
||||
log_info->disqualified_at = it->second;
|
||||
}
|
||||
log_list_mojo.push_back(std::move(log_info));
|
||||
}
|
||||
network_service->UpdateCtLogList(std::move(log_list_mojo));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Configure the stub resolver. This must be done after the system
|
||||
// NetworkContext is created, but before anything has the chance to use it.
|
||||
stub_resolver_config_reader_.UpdateNetworkService(true /* record_metrics */);
|
||||
@ -650,32 +681,8 @@ void SystemNetworkContextManager::ConfigureDefaultNetworkContextParams(
|
||||
if (g_enable_certificate_transparency) {
|
||||
network_context_params->enforce_chrome_ct_policy = true;
|
||||
network_context_params->ct_log_update_time = base::GetBuildTime();
|
||||
|
||||
std::vector<std::string> operated_by_google_logs =
|
||||
certificate_transparency::GetLogsOperatedByGoogle();
|
||||
std::vector<std::pair<std::string, base::TimeDelta>> disqualified_logs =
|
||||
certificate_transparency::GetDisqualifiedLogs();
|
||||
for (const auto& ct_log : certificate_transparency::GetKnownLogs()) {
|
||||
// TODO(rsleevi): https://crbug.com/702062 - Remove this duplication.
|
||||
network::mojom::CTLogInfoPtr log_info = network::mojom::CTLogInfo::New();
|
||||
log_info->public_key = std::string(ct_log.log_key, ct_log.log_key_length);
|
||||
log_info->name = ct_log.log_name;
|
||||
|
||||
std::string log_id = crypto::SHA256HashString(log_info->public_key);
|
||||
log_info->operated_by_google =
|
||||
std::binary_search(std::begin(operated_by_google_logs),
|
||||
std::end(operated_by_google_logs), log_id);
|
||||
auto it = std::lower_bound(
|
||||
std::begin(disqualified_logs), std::end(disqualified_logs), log_id,
|
||||
[](const auto& disqualified_log, const std::string& log_id) {
|
||||
return disqualified_log.first < log_id;
|
||||
});
|
||||
if (it != std::end(disqualified_logs) && it->first == log_id) {
|
||||
log_info->disqualified_at = it->second;
|
||||
}
|
||||
network_context_params->ct_logs.push_back(std::move(log_info));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
|
||||
|
@ -445,44 +445,6 @@ class SystemNetworkContextManagerCertificateTransparencyBrowsertest
|
||||
}
|
||||
};
|
||||
|
||||
#if BUILDFLAG(IS_CT_SUPPORTED)
|
||||
IN_PROC_BROWSER_TEST_P(
|
||||
SystemNetworkContextManagerCertificateTransparencyBrowsertest,
|
||||
CertificateTransparencyConfig) {
|
||||
network::mojom::NetworkContextParamsPtr context_params =
|
||||
g_browser_process->system_network_context_manager()
|
||||
->CreateDefaultNetworkContextParams();
|
||||
|
||||
const bool kDefault =
|
||||
#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && defined(OFFICIAL_BUILD) && \
|
||||
!defined(OS_ANDROID)
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
EXPECT_EQ(GetParam().value_or(kDefault),
|
||||
context_params->enforce_chrome_ct_policy);
|
||||
EXPECT_NE(GetParam().value_or(kDefault), context_params->ct_logs.empty());
|
||||
|
||||
if (GetParam().value_or(kDefault)) {
|
||||
bool has_google_log = false;
|
||||
bool has_disqualified_log = false;
|
||||
for (const auto& ct_log : context_params->ct_logs) {
|
||||
has_google_log |= ct_log->operated_by_google;
|
||||
has_disqualified_log |= ct_log->disqualified_at.has_value();
|
||||
}
|
||||
EXPECT_TRUE(has_google_log);
|
||||
EXPECT_TRUE(has_disqualified_log);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
All,
|
||||
SystemNetworkContextManagerCertificateTransparencyBrowsertest,
|
||||
::testing::Values(absl::nullopt, true, false));
|
||||
|
||||
#if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
|
||||
class SystemNetworkContextServiceCertVerifierBuiltinPermissionsPolicyTest
|
||||
: public policy::PolicyTest,
|
||||
|
@ -2,12 +2,24 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("//third_party/protobuf/proto_library.gni")
|
||||
|
||||
proto_library("proto") {
|
||||
sources = [
|
||||
"certificate_transparency.proto",
|
||||
"certificate_transparency_config.proto",
|
||||
"ct_timestamp.proto",
|
||||
]
|
||||
}
|
||||
|
||||
static_library("certificate_transparency") {
|
||||
sources = [
|
||||
"chrome_ct_policy_enforcer.cc",
|
||||
"chrome_ct_policy_enforcer.h",
|
||||
"chrome_require_ct_delegate.cc",
|
||||
"chrome_require_ct_delegate.h",
|
||||
"ct_features.cc",
|
||||
"ct_features.h",
|
||||
"ct_known_logs.cc",
|
||||
"ct_known_logs.h",
|
||||
"pref_names.cc",
|
||||
@ -24,6 +36,8 @@ static_library("certificate_transparency") {
|
||||
"//net",
|
||||
"//url",
|
||||
]
|
||||
|
||||
public_deps = [ ":proto" ]
|
||||
}
|
||||
|
||||
source_set("unit_tests") {
|
||||
|
@ -0,0 +1,116 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package chrome_browser_certificate_transparency;
|
||||
|
||||
import "ct_timestamp.proto";
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
// Represents the final state of a log at the time it was made read-only.
|
||||
message FinalTreeHead {
|
||||
// Size of the log at the time it was made read-only.
|
||||
uint64 tree_size = 1;
|
||||
// Root hash of the log (base64-encoded) at the time it was made read-only.
|
||||
string sha256_root_hash = 2;
|
||||
}
|
||||
|
||||
message CTLog {
|
||||
// Human-readable description to identify log.
|
||||
string description = 1;
|
||||
// Public key of the log, as a DER-encoded ASN.1 SubjectPublicKeyInfo
|
||||
// structure, then encoded as base64
|
||||
// (https://tools.ietf.org/html/rfc5280#section-4.1.2.7).
|
||||
string key = 2;
|
||||
// The base64-encoded LogID found in SCTs issued by this log
|
||||
// (https://tools.ietf.org/html/rfc6962#section-3.2).
|
||||
string log_id = 3;
|
||||
// Maximum merge delay, in seconds. The log should not take longer than this
|
||||
// to incorporate a certificate.
|
||||
uint64 mmd_secs = 4;
|
||||
// URL of the log's HTTP API.
|
||||
string url = 5;
|
||||
|
||||
message Interval {
|
||||
CTTimestamp start = 1;
|
||||
CTTimestamp end = 2;
|
||||
}
|
||||
// The log will only accept certificates that expire between those dates.
|
||||
// Start time is inclusive, end time is not inclusive.
|
||||
Interval temporal_interval = 6;
|
||||
|
||||
enum Purpose {
|
||||
UNSET_PURPOSE = 0;
|
||||
PROD = 1;
|
||||
TEST = 2;
|
||||
}
|
||||
// Whether the log is for production purposes, or test only.
|
||||
Purpose purpose = 7;
|
||||
|
||||
enum CurrentState {
|
||||
UNSET_STATE = 0;
|
||||
PENDING = 1;
|
||||
QUALIFIED = 2;
|
||||
USABLE = 3;
|
||||
READ_ONLY = 4;
|
||||
RETIRED = 5;
|
||||
REJECTED = 6;
|
||||
}
|
||||
message State {
|
||||
// Current state of the log.
|
||||
CurrentState current_state = 1;
|
||||
// Time at which the log entered this state.
|
||||
CTTimestamp state_start = 2;
|
||||
}
|
||||
// State history of the log. Inverse chronological order, first element should
|
||||
// be the current state.
|
||||
repeated State state = 8;
|
||||
|
||||
message OperatorChange {
|
||||
// Name of the log operator.
|
||||
string name = 1;
|
||||
// Timestamp at which this operator started operating this log.
|
||||
CTTimestamp operator_start = 2;
|
||||
}
|
||||
// History of all log operators that have ever operated this log, including
|
||||
// the timestamp at which each started operating it. Inverse chronological
|
||||
// order, first element should be the current operator.
|
||||
repeated OperatorChange operator_history = 9;
|
||||
|
||||
// State of the log at the time it was made read-only. Should only be set if
|
||||
// state is READ_ONLY.
|
||||
FinalTreeHead read_only_info = 16;
|
||||
}
|
||||
|
||||
message LogOperator {
|
||||
// Name of this log operator.
|
||||
string name = 1;
|
||||
// Email addresses at which the log operator can be reached.
|
||||
repeated string email = 2;
|
||||
}
|
||||
|
||||
message CTLogList {
|
||||
// Major version of the list, incremented any time there are changes in the
|
||||
// list, except for trivial (i.e. timestamp-only) changes.
|
||||
uint64 list_version_major = 1;
|
||||
// Minor version of the list, incremented any time the list is modified with
|
||||
// only trivial (i.e. timestamp-only) changes. Allows consumers to determine
|
||||
// the timestamp at which certain changes occur; for example, if a log is
|
||||
// rejected, a consumer can look at the minor version 1 of that major version
|
||||
// to determine at what timestamp that change was made.
|
||||
uint64 list_version_minor = 2;
|
||||
// Log list timestamp. This is meant to be used for freshness checks, and is
|
||||
// updated periodically regardless of whether the list contents' have changed.
|
||||
// Use list_version_major instead if monitoring for list contents' changes.
|
||||
CTTimestamp timestamp = 3;
|
||||
// Compatibility version, incremented if the list structure is changed in a
|
||||
// non-backwards-compatible way.
|
||||
uint64 compatibility_version = 4;
|
||||
// Contains all known log operators.
|
||||
repeated LogOperator operators = 5;
|
||||
// Contains all known logs.
|
||||
repeated CTLog logs = 6;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package chrome_browser_certificate_transparency;
|
||||
|
||||
import "certificate_transparency.proto";
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
// Certificate transparency configuration as used by Chrome.
|
||||
message CTConfig {
|
||||
// Emergency switch to disable all CT enforcement.
|
||||
bool disable_ct_enforcement = 1;
|
||||
// Logs Chrome should recognize.
|
||||
CTLogList log_list = 2;
|
||||
}
|
15
components/certificate_transparency/ct_features.cc
Normal file
15
components/certificate_transparency/ct_features.cc
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2021 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 "components/certificate_transparency/ct_features.h"
|
||||
|
||||
namespace certificate_transparency {
|
||||
namespace features {
|
||||
|
||||
const base::Feature kCertificateTransparencyComponentUpdater{
|
||||
"CertificateTransparencyComponentUpdater",
|
||||
base::FEATURE_DISABLED_BY_DEFAULT};
|
||||
|
||||
} // namespace features
|
||||
} // namespace certificate_transparency
|
18
components/certificate_transparency/ct_features.h
Normal file
18
components/certificate_transparency/ct_features.h
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#ifndef COMPONENTS_CERTIFICATE_TRANSPARENCY_CT_FEATURES_H_
|
||||
#define COMPONENTS_CERTIFICATE_TRANSPARENCY_CT_FEATURES_H_
|
||||
|
||||
#include "base/feature_list.h"
|
||||
|
||||
namespace certificate_transparency {
|
||||
namespace features {
|
||||
|
||||
extern const base::Feature kCertificateTransparencyComponentUpdater;
|
||||
|
||||
} // namespace features
|
||||
} // namespace certificate_transparency
|
||||
|
||||
#endif // COMPONENTS_CERTIFICATE_TRANSPARENCY_CT_FEATURES_H_
|
15
components/certificate_transparency/ct_timestamp.proto
Normal file
15
components/certificate_transparency/ct_timestamp.proto
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package chrome_browser_certificate_transparency;
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
message CTTimestamp {
|
||||
int64 seconds = 1;
|
||||
|
||||
int32 nanos = 2;
|
||||
}
|
@ -45,6 +45,8 @@ class NET_EXPORT CertAndCTVerifier : public CertVerifier {
|
||||
const NetLogWithSource& net_log,
|
||||
int result);
|
||||
|
||||
// TODO(crbug.com/1211074): Expose CT log list as part of
|
||||
// CertVerifier::Config.
|
||||
std::unique_ptr<CertVerifier> cert_verifier_;
|
||||
std::unique_ptr<CTVerifier> ct_verifier_;
|
||||
};
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/metrics/histogram_macros.h"
|
||||
#include "base/values.h"
|
||||
@ -52,15 +54,37 @@ void AddSCTAndLogStatus(scoped_refptr<ct::SignedCertificateTimestamp> sct,
|
||||
|
||||
} // namespace
|
||||
|
||||
MultiLogCTVerifier::MultiLogCTVerifier() {}
|
||||
base::CallbackListSubscription
|
||||
MultiLogCTVerifier::CTLogProvider::RegisterLogsListCallback(
|
||||
LogListCallbackList::CallbackType callback) {
|
||||
return callback_list_.Add(std::move(callback));
|
||||
}
|
||||
|
||||
void MultiLogCTVerifier::CTLogProvider::NotifyCallbacks(
|
||||
const std::vector<scoped_refptr<const net::CTLogVerifier>>& log_verifiers) {
|
||||
callback_list_.Notify(log_verifiers);
|
||||
}
|
||||
|
||||
MultiLogCTVerifier::CTLogProvider::CTLogProvider() = default;
|
||||
MultiLogCTVerifier::CTLogProvider::~CTLogProvider() = default;
|
||||
|
||||
MultiLogCTVerifier::MultiLogCTVerifier(CTLogProvider* notifier) {
|
||||
// base::Unretained is safe since we are using a CallbackListSubscription that
|
||||
// won't outlive |this|.
|
||||
log_provider_subscription_ =
|
||||
notifier->RegisterLogsListCallback(base::BindRepeating(
|
||||
&MultiLogCTVerifier::SetLogs, base::Unretained(this)));
|
||||
}
|
||||
|
||||
MultiLogCTVerifier::~MultiLogCTVerifier() = default;
|
||||
|
||||
void MultiLogCTVerifier::AddLogs(
|
||||
void MultiLogCTVerifier::SetLogs(
|
||||
const std::vector<scoped_refptr<const CTLogVerifier>>& log_verifiers) {
|
||||
logs_.clear();
|
||||
for (const auto& log_verifier : log_verifiers) {
|
||||
VLOG(1) << "Adding CT log: " << log_verifier->description();
|
||||
logs_[log_verifier->key_id()] = log_verifier;
|
||||
std::string key_id = log_verifier->key_id();
|
||||
logs_[key_id] = log_verifier;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,8 +8,11 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "base/callback_forward.h"
|
||||
#include "base/callback_list.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "net/base/net_export.h"
|
||||
#include "net/cert/ct_verifier.h"
|
||||
@ -28,10 +31,30 @@ class CTLogVerifier;
|
||||
// It must be initialized with a list of logs by calling AddLogs.
|
||||
class NET_EXPORT MultiLogCTVerifier : public CTVerifier {
|
||||
public:
|
||||
MultiLogCTVerifier();
|
||||
class NET_EXPORT CTLogProvider {
|
||||
public:
|
||||
using LogListCallbackList = base::RepeatingCallbackList<void(
|
||||
const std::vector<scoped_refptr<const CTLogVerifier>>& log_verifiers)>;
|
||||
|
||||
base::CallbackListSubscription RegisterLogsListCallback(
|
||||
LogListCallbackList::CallbackType callback);
|
||||
|
||||
protected:
|
||||
CTLogProvider();
|
||||
~CTLogProvider();
|
||||
|
||||
void NotifyCallbacks(
|
||||
const std::vector<scoped_refptr<const net::CTLogVerifier>>&
|
||||
log_verifiers);
|
||||
|
||||
private:
|
||||
LogListCallbackList callback_list_;
|
||||
};
|
||||
|
||||
explicit MultiLogCTVerifier(CTLogProvider* notifier);
|
||||
~MultiLogCTVerifier() override;
|
||||
|
||||
void AddLogs(
|
||||
void SetLogs(
|
||||
const std::vector<scoped_refptr<const CTLogVerifier>>& log_verifiers);
|
||||
|
||||
// CTVerifier implementation:
|
||||
@ -65,6 +88,8 @@ class NET_EXPORT MultiLogCTVerifier : public CTVerifier {
|
||||
// of RFC6962.
|
||||
std::map<std::string, scoped_refptr<const CTLogVerifier>> logs_;
|
||||
|
||||
base::CallbackListSubscription log_provider_subscription_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MultiLogCTVerifier);
|
||||
};
|
||||
|
||||
|
@ -41,6 +41,12 @@ namespace {
|
||||
const char kHostname[] = "example.com";
|
||||
const char kLogDescription[] = "somelog";
|
||||
|
||||
class DoNothingLogProvider : public MultiLogCTVerifier::CTLogProvider {
|
||||
public:
|
||||
DoNothingLogProvider() = default;
|
||||
~DoNothingLogProvider() = default;
|
||||
};
|
||||
|
||||
class MultiLogCTVerifierTest : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
@ -49,8 +55,9 @@ class MultiLogCTVerifierTest : public ::testing::Test {
|
||||
ASSERT_TRUE(log);
|
||||
log_verifiers_.push_back(log);
|
||||
|
||||
verifier_ = std::make_unique<MultiLogCTVerifier>();
|
||||
verifier_->AddLogs(log_verifiers_);
|
||||
DoNothingLogProvider notifier;
|
||||
verifier_ = std::make_unique<MultiLogCTVerifier>(¬ifier);
|
||||
verifier_->SetLogs(log_verifiers_);
|
||||
std::string der_test_cert(ct::GetDerEncodedX509Cert());
|
||||
chain_ = X509Certificate::CreateFromBytes(
|
||||
der_test_cert.data(),
|
||||
@ -241,6 +248,29 @@ TEST_F(MultiLogCTVerifierTest, CountsSingleEmbeddedSCTInOriginsHistogram) {
|
||||
EXPECT_EQ(old_embedded_count + 1, NumEmbeddedSCTsInHistogram());
|
||||
}
|
||||
|
||||
TEST_F(MultiLogCTVerifierTest, SetLogsRemovesOldLogs) {
|
||||
log_verifiers_.clear();
|
||||
verifier_->SetLogs(log_verifiers_);
|
||||
// Log list is now empty so verification should fail.
|
||||
ASSERT_FALSE(CheckPrecertificateVerification(embedded_sct_chain_));
|
||||
}
|
||||
|
||||
TEST_F(MultiLogCTVerifierTest, SetLogsAddsNewLogs) {
|
||||
// Clear the log list.
|
||||
log_verifiers_.clear();
|
||||
verifier_->SetLogs(log_verifiers_);
|
||||
|
||||
// Add valid log again via SetLogs
|
||||
scoped_refptr<const CTLogVerifier> log(
|
||||
CTLogVerifier::Create(ct::GetTestPublicKey(), kLogDescription));
|
||||
ASSERT_TRUE(log);
|
||||
log_verifiers_.push_back(log);
|
||||
verifier_->SetLogs(log_verifiers_);
|
||||
|
||||
// Verification should now succeed.
|
||||
ASSERT_TRUE(CheckPrecertificateVerification(embedded_sct_chain_));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace net
|
||||
|
@ -300,6 +300,12 @@ TEST_F(ProofVerifierChromiumTest, FailsIfCertFails) {
|
||||
ASSERT_EQ(quic::QUIC_FAILURE, status);
|
||||
}
|
||||
|
||||
class DoNothingLogNotifier : public MultiLogCTVerifier::CTLogProvider {
|
||||
public:
|
||||
DoNothingLogNotifier() = default;
|
||||
~DoNothingLogNotifier() = default;
|
||||
};
|
||||
|
||||
// Valid SCT and cert
|
||||
TEST_F(ProofVerifierChromiumTest, ValidSCTList) {
|
||||
// Use different certificates for SCT tests.
|
||||
@ -322,8 +328,9 @@ TEST_F(ProofVerifierChromiumTest, ValidSCTList) {
|
||||
CTLogVerifier::Create(ct::GetTestPublicKey(), kLogDescription));
|
||||
ASSERT_TRUE(log);
|
||||
log_verifiers.push_back(log);
|
||||
auto ct_verifier = std::make_unique<MultiLogCTVerifier>();
|
||||
ct_verifier->AddLogs(log_verifiers);
|
||||
DoNothingLogNotifier notifier;
|
||||
auto ct_verifier = std::make_unique<MultiLogCTVerifier>(¬ifier);
|
||||
ct_verifier->SetLogs(log_verifiers);
|
||||
|
||||
CertAndCTVerifier cert_verifier(std::move(dummy_verifier),
|
||||
std::move(ct_verifier));
|
||||
@ -364,8 +371,9 @@ TEST_F(ProofVerifierChromiumTest, InvalidSCTList) {
|
||||
CTLogVerifier::Create(ct::GetTestPublicKey(), kLogDescription));
|
||||
ASSERT_TRUE(log);
|
||||
log_verifiers.push_back(log);
|
||||
auto ct_verifier = std::make_unique<MultiLogCTVerifier>();
|
||||
ct_verifier->AddLogs(log_verifiers);
|
||||
DoNothingLogNotifier notifier;
|
||||
auto ct_verifier = std::make_unique<MultiLogCTVerifier>(¬ifier);
|
||||
ct_verifier->SetLogs(log_verifiers);
|
||||
|
||||
CertAndCTVerifier cert_verifier(std::move(dummy_verifier),
|
||||
std::move(ct_verifier));
|
||||
|
@ -257,6 +257,8 @@ component("network_service") {
|
||||
|
||||
if (is_ct_supported) {
|
||||
sources += [
|
||||
"ct_log_list_distributor.cc",
|
||||
"ct_log_list_distributor.h",
|
||||
"expect_ct_reporter.cc",
|
||||
"expect_ct_reporter.h",
|
||||
"sct_auditing/sct_auditing_cache.cc",
|
||||
@ -425,6 +427,7 @@ source_set("tests") {
|
||||
|
||||
if (is_ct_supported) {
|
||||
sources += [
|
||||
"ct_log_list_distributor_unittest.cc",
|
||||
"expect_ct_reporter_unittest.cc",
|
||||
"sct_auditing/sct_auditing_cache_unittest.cc",
|
||||
]
|
||||
|
36
services/network/ct_log_list_distributor.cc
Normal file
36
services/network/ct_log_list_distributor.cc
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2021 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 "services/network/ct_log_list_distributor.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/callback_list.h"
|
||||
#include "net/cert/ct_log_verifier.h"
|
||||
#include "services/network/public/cpp/features.h"
|
||||
|
||||
namespace network {
|
||||
|
||||
CtLogListDistributor::CtLogListDistributor() = default;
|
||||
|
||||
CtLogListDistributor::~CtLogListDistributor() = default;
|
||||
|
||||
void CtLogListDistributor::OnNewCtConfig(
|
||||
const std::vector<mojom::CTLogInfoPtr>& log_list) {
|
||||
std::vector<scoped_refptr<const net::CTLogVerifier>> ct_logs;
|
||||
for (auto& log : log_list) {
|
||||
scoped_refptr<const net::CTLogVerifier> log_verifier =
|
||||
net::CTLogVerifier::Create(log->public_key, log->name);
|
||||
if (!log_verifier) {
|
||||
// TODO(crbug.com/1211056): Signal bad configuration (such as bad key).
|
||||
continue;
|
||||
}
|
||||
ct_logs.push_back(std::move(log_verifier));
|
||||
}
|
||||
|
||||
NotifyCallbacks(ct_logs);
|
||||
}
|
||||
|
||||
} // namespace network
|
33
services/network/ct_log_list_distributor.h
Normal file
33
services/network/ct_log_list_distributor.h
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#ifndef SERVICES_NETWORK_CT_LOG_LIST_DISTRIBUTOR_H_
|
||||
#define SERVICES_NETWORK_CT_LOG_LIST_DISTRIBUTOR_H_
|
||||
|
||||
#include "base/callback_forward.h"
|
||||
#include "base/callback_list.h"
|
||||
#include "base/component_export.h"
|
||||
#include "net/cert/multi_log_ct_verifier.h"
|
||||
#include "services/network/public/mojom/ct_log_info.mojom.h"
|
||||
|
||||
namespace network {
|
||||
|
||||
// Handles distribution of component updater delivered Certificate
|
||||
// Transparency enforcement configuration to classes that need to be
|
||||
// notified about changes.
|
||||
|
||||
// TODO(crbug.com/1211074): Once CT log list configuration is exposed via
|
||||
// CertVerifier::Config, we should configure changes using that instead.
|
||||
class COMPONENT_EXPORT(NETWORK_SERVICE) CtLogListDistributor
|
||||
: public net::MultiLogCTVerifier::CTLogProvider {
|
||||
public:
|
||||
CtLogListDistributor();
|
||||
virtual ~CtLogListDistributor();
|
||||
|
||||
void OnNewCtConfig(const std::vector<mojom::CTLogInfoPtr>& log_list);
|
||||
};
|
||||
|
||||
} // namespace network
|
||||
|
||||
#endif // SERVICES_NETWORK_CT_LOG_LIST_DISTRIBUTOR_H_
|
81
services/network/ct_log_list_distributor_unittest.cc
Normal file
81
services/network/ct_log_list_distributor_unittest.cc
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2021 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 "base/logging.h"
|
||||
|
||||
#include "services/network/ct_log_list_distributor.h"
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/callback_list.h"
|
||||
#include "base/test/task_environment.h"
|
||||
#include "crypto/sha2.h"
|
||||
#include "net/cert/ct_log_verifier.h"
|
||||
#include "net/cert/multi_log_ct_verifier.h"
|
||||
#include "net/test/ct_test_util.h"
|
||||
#include "services/network/public/mojom/ct_log_info.mojom.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace network {
|
||||
|
||||
class MockLogVerifier {
|
||||
public:
|
||||
MockLogVerifier() = default;
|
||||
~MockLogVerifier() = default;
|
||||
|
||||
void SetLogs(const std::vector<scoped_refptr<const net::CTLogVerifier>>&
|
||||
log_verifiers) {
|
||||
logs_ = log_verifiers;
|
||||
}
|
||||
|
||||
std::vector<scoped_refptr<const net::CTLogVerifier>> GetLogs() {
|
||||
return logs_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<scoped_refptr<const net::CTLogVerifier>> logs_;
|
||||
};
|
||||
|
||||
class CtLogListDistributorTest : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
subscription_ = distributor_.RegisterLogsListCallback(base::BindRepeating(
|
||||
&MockLogVerifier::SetLogs, base::Unretained(&verifier_)));
|
||||
}
|
||||
void Wait() { task_environment_.RunUntilIdle(); }
|
||||
|
||||
protected:
|
||||
base::test::TaskEnvironment task_environment_{
|
||||
base::test::TaskEnvironment::MainThreadType::DEFAULT,
|
||||
base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED};
|
||||
CtLogListDistributor distributor_;
|
||||
MockLogVerifier verifier_;
|
||||
base::CallbackListSubscription subscription_;
|
||||
};
|
||||
|
||||
TEST_F(CtLogListDistributorTest, TestOnNewCtConfig) {
|
||||
const char kLogDescription[] = "somelog";
|
||||
|
||||
// Create log list with a single log.
|
||||
std::vector<network::mojom::CTLogInfoPtr> log_list_mojo;
|
||||
network::mojom::CTLogInfoPtr log_ptr = network::mojom::CTLogInfo::New();
|
||||
log_ptr->name = kLogDescription;
|
||||
log_ptr->public_key = net::ct::GetTestPublicKey();
|
||||
log_list_mojo.push_back(std::move(log_ptr));
|
||||
|
||||
// Pass the log list to the distributor.
|
||||
distributor_.OnNewCtConfig(log_list_mojo);
|
||||
|
||||
// Wait for parsing to finish.
|
||||
Wait();
|
||||
|
||||
// Verifier should have been notified and have the log list.
|
||||
std::vector<scoped_refptr<const net::CTLogVerifier>> logs =
|
||||
verifier_.GetLogs();
|
||||
EXPECT_EQ(logs.size(), 1u);
|
||||
EXPECT_EQ(logs[0]->description(), kLogDescription);
|
||||
EXPECT_EQ(logs[0]->key_id(),
|
||||
crypto::SHA256HashString(net::ct::GetTestPublicKey()));
|
||||
}
|
||||
|
||||
} // namespace network
|
@ -124,6 +124,7 @@
|
||||
#include "net/cert/cert_and_ct_verifier.h"
|
||||
#include "net/cert/ct_log_verifier.h"
|
||||
#include "net/cert/multi_log_ct_verifier.h"
|
||||
#include "services/network/ct_log_list_distributor.h"
|
||||
#include "services/network/expect_ct_reporter.h"
|
||||
#include "services/network/sct_auditing/sct_auditing_cache.h"
|
||||
#endif // BUILDFLAG(IS_CT_SUPPORTED)
|
||||
@ -1900,21 +1901,20 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext(
|
||||
|
||||
#if BUILDFLAG(IS_CT_SUPPORTED)
|
||||
std::vector<scoped_refptr<const net::CTLogVerifier>> ct_logs;
|
||||
if (!params_->ct_logs.empty()) {
|
||||
for (const auto& log : params_->ct_logs) {
|
||||
scoped_refptr<const net::CTLogVerifier> log_verifier =
|
||||
net::CTLogVerifier::Create(log->public_key, log->name);
|
||||
if (!log_verifier) {
|
||||
// TODO: Signal bad configuration (such as bad key).
|
||||
continue;
|
||||
}
|
||||
ct_logs.push_back(std::move(log_verifier));
|
||||
for (const auto& log : network_service_->log_list()) {
|
||||
scoped_refptr<const net::CTLogVerifier> log_verifier =
|
||||
net::CTLogVerifier::Create(log->public_key, log->name);
|
||||
if (!log_verifier) {
|
||||
// TODO: Signal bad configuration (such as bad key).
|
||||
continue;
|
||||
}
|
||||
auto ct_verifier = std::make_unique<net::MultiLogCTVerifier>();
|
||||
ct_verifier->AddLogs(ct_logs);
|
||||
cert_verifier = std::make_unique<net::CertAndCTVerifier>(
|
||||
std::move(cert_verifier), std::move(ct_verifier));
|
||||
ct_logs.push_back(std::move(log_verifier));
|
||||
}
|
||||
auto ct_verifier = std::make_unique<net::MultiLogCTVerifier>(
|
||||
network_service_->ct_log_list_distributor());
|
||||
ct_verifier->SetLogs(ct_logs);
|
||||
cert_verifier = std::make_unique<net::CertAndCTVerifier>(
|
||||
std::move(cert_verifier), std::move(ct_verifier));
|
||||
#endif // BUILDFLAG(IS_CT_SUPPORTED)
|
||||
|
||||
// Whether the cert verifier is remote or in-process, we should wrap it in
|
||||
@ -1942,16 +1942,13 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext(
|
||||
if (params_->enforce_chrome_ct_policy) {
|
||||
std::vector<std::pair<std::string, base::TimeDelta>> disqualified_logs;
|
||||
std::vector<std::string> operated_by_google_logs;
|
||||
if (!params_->ct_logs.empty()) {
|
||||
for (const auto& log : params_->ct_logs) {
|
||||
if (log->operated_by_google || log->disqualified_at) {
|
||||
std::string log_id = crypto::SHA256HashString(log->public_key);
|
||||
if (log->operated_by_google)
|
||||
operated_by_google_logs.push_back(log_id);
|
||||
if (log->disqualified_at) {
|
||||
disqualified_logs.push_back(
|
||||
std::make_pair(log_id, log->disqualified_at.value()));
|
||||
}
|
||||
for (const auto& log : network_service_->log_list()) {
|
||||
if (log->operated_by_google || log->disqualified_at) {
|
||||
std::string log_id = crypto::SHA256HashString(log->public_key);
|
||||
if (log->operated_by_google)
|
||||
operated_by_google_logs.push_back(log_id);
|
||||
if (log->disqualified_at) {
|
||||
disqualified_logs.emplace_back(log_id, log->disqualified_at.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6040,9 +6040,8 @@ TEST_F(NetworkContextTest, AddFtpAuthCacheEntry) {
|
||||
|
||||
#if BUILDFLAG(IS_CT_SUPPORTED)
|
||||
TEST_F(NetworkContextTest, CertificateTransparencyConfig) {
|
||||
mojom::NetworkContextParamsPtr params = CreateContextParams();
|
||||
params->enforce_chrome_ct_policy = true;
|
||||
params->ct_log_update_time = base::Time::Now();
|
||||
// Configure CT logs in network service.
|
||||
std::vector<network::mojom::CTLogInfoPtr> log_list_mojo;
|
||||
|
||||
// The log public keys do not matter for the test, so invalid keys are used.
|
||||
// However, because the log IDs are derived from the SHA-256 hash of the log
|
||||
@ -6057,7 +6056,7 @@ TEST_F(NetworkContextTest, CertificateTransparencyConfig) {
|
||||
log_info->name = std::string(4, 0x30 + static_cast<char>(i));
|
||||
log_info->operated_by_google = i % 2;
|
||||
|
||||
params->ct_logs.push_back(std::move(log_info));
|
||||
log_list_mojo.push_back(std::move(log_info));
|
||||
}
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
network::mojom::CTLogInfoPtr log_info = network::mojom::CTLogInfo::New();
|
||||
@ -6067,8 +6066,15 @@ TEST_F(NetworkContextTest, CertificateTransparencyConfig) {
|
||||
log_info->operated_by_google = false;
|
||||
log_info->disqualified_at = base::TimeDelta::FromSeconds(i);
|
||||
|
||||
params->ct_logs.push_back(std::move(log_info));
|
||||
log_list_mojo.push_back(std::move(log_info));
|
||||
}
|
||||
network_service()->UpdateCtLogList(std::move(log_list_mojo));
|
||||
|
||||
// Configure CT params in network context.
|
||||
mojom::NetworkContextParamsPtr params = CreateContextParams();
|
||||
params->enforce_chrome_ct_policy = true;
|
||||
params->ct_log_update_time = base::Time::Now();
|
||||
|
||||
std::unique_ptr<NetworkContext> network_context =
|
||||
CreateContextWithParams(std::move(params));
|
||||
|
||||
|
@ -83,6 +83,8 @@
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_CT_SUPPORTED)
|
||||
#include "components/certificate_transparency/ct_features.h"
|
||||
#include "services/network/ct_log_list_distributor.h"
|
||||
#include "services/network/sct_auditing/sct_auditing_cache.h"
|
||||
#endif
|
||||
|
||||
@ -326,6 +328,10 @@ void NetworkService::Initialize(mojom::NetworkServiceParamsPtr params,
|
||||
|
||||
crl_set_distributor_ = std::make_unique<CRLSetDistributor>();
|
||||
|
||||
#if BUILDFLAG(IS_CT_SUPPORTED)
|
||||
ct_log_list_distributor_ = std::make_unique<CtLogListDistributor>();
|
||||
#endif
|
||||
|
||||
doh_probe_activator_ = std::make_unique<DelayedDohProbeActivator>(this);
|
||||
|
||||
trust_token_key_commitments_ = std::make_unique<TrustTokenKeyCommitments>();
|
||||
@ -715,7 +721,25 @@ void NetworkService::ConfigureSCTAuditing(
|
||||
sct_auditing_cache_->set_traffic_annotation(traffic_annotation);
|
||||
sct_auditing_cache_->set_url_loader_factory(std::move(factory));
|
||||
}
|
||||
#endif
|
||||
|
||||
void NetworkService::UpdateCtLogList(
|
||||
std::vector<mojom::CTLogInfoPtr> log_list) {
|
||||
log_list_ = std::move(log_list);
|
||||
if (base::FeatureList::IsEnabled(
|
||||
certificate_transparency::features::
|
||||
kCertificateTransparencyComponentUpdater)) {
|
||||
ct_log_list_distributor_->OnNewCtConfig(log_list_);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkService::SetCtEnforcementEnabled(bool enabled) {
|
||||
// TODO(crbug.com/1211535): Implement Certificate Transparency killswitch.
|
||||
DCHECK(base::FeatureList::IsEnabled(
|
||||
certificate_transparency::features::
|
||||
kCertificateTransparencyComponentUpdater));
|
||||
}
|
||||
|
||||
#endif // BUILDFLAG(IS_CT_SUPPORTED)
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
void NetworkService::DumpWithoutCrashing(base::Time dump_request_time) {
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "base/containers/flat_set.h"
|
||||
#include "base/containers/span.h"
|
||||
#include "base/containers/unique_ptr_adapters.h"
|
||||
#include "base/feature_list.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "base/time/time.h"
|
||||
@ -47,6 +48,10 @@
|
||||
#include "services/service_manager/public/cpp/binder_registry.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
#if BUILDFLAG(IS_CT_SUPPORTED)
|
||||
#include "services/network/public/mojom/ct_log_info.mojom.h"
|
||||
#endif
|
||||
|
||||
namespace net {
|
||||
class FileNetLogObserver;
|
||||
class HostResolverManager;
|
||||
@ -59,6 +64,7 @@ class URLRequestContext;
|
||||
namespace network {
|
||||
|
||||
class CRLSetDistributor;
|
||||
class CtLogListDistributor;
|
||||
class DnsConfigChangeManager;
|
||||
class HttpAuthCacheCopier;
|
||||
class NetLogProxySink;
|
||||
@ -184,6 +190,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService
|
||||
const GURL& reporting_uri,
|
||||
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
|
||||
mojo::PendingRemote<mojom::URLLoaderFactory> factory) override;
|
||||
void UpdateCtLogList(std::vector<mojom::CTLogInfoPtr> log_list) override;
|
||||
void SetCtEnforcementEnabled(bool enabled) override;
|
||||
#endif
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
@ -225,6 +233,12 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService
|
||||
return crl_set_distributor_.get();
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_CT_SUPPORTED)
|
||||
CtLogListDistributor* ct_log_list_distributor() {
|
||||
return ct_log_list_distributor_.get();
|
||||
}
|
||||
#endif
|
||||
|
||||
const FirstPartySets* first_party_sets() const {
|
||||
return first_party_sets_.get();
|
||||
}
|
||||
@ -250,6 +264,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService
|
||||
|
||||
#if BUILDFLAG(IS_CT_SUPPORTED)
|
||||
SCTAuditingCache* sct_auditing_cache() { return sct_auditing_cache_.get(); }
|
||||
|
||||
const std::vector<mojom::CTLogInfoPtr>& log_list() const { return log_list_; }
|
||||
#endif
|
||||
|
||||
mojom::URLLoaderNetworkServiceObserver*
|
||||
@ -352,6 +368,10 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService
|
||||
|
||||
#if BUILDFLAG(IS_CT_SUPPORTED)
|
||||
std::unique_ptr<SCTAuditingCache> sct_auditing_cache_;
|
||||
|
||||
std::vector<mojom::CTLogInfoPtr> log_list_;
|
||||
|
||||
std::unique_ptr<CtLogListDistributor> ct_log_list_distributor_;
|
||||
#endif
|
||||
|
||||
// Map from a renderer process id, to the set of plugin origins embedded by
|
||||
|
@ -57,9 +57,6 @@ import "url/mojom/url.mojom";
|
||||
[EnableIf=is_chromeos_ash]
|
||||
import "services/network/public/mojom/dhcp_wpad_url_client.mojom";
|
||||
|
||||
[EnableIf=is_ct_supported]
|
||||
import "services/network/public/mojom/ct_log_info.mojom";
|
||||
|
||||
const uint32 kWebSocketOptionNone = 0;
|
||||
// Disallow the request from sending cookies. Disallow the response from writing
|
||||
// cookies.
|
||||
@ -363,18 +360,12 @@ struct NetworkContextParams {
|
||||
[EnableIf=is_ct_supported]
|
||||
bool enable_sct_auditing = false;
|
||||
|
||||
// The Certificate Transparency logs that are known to the client. SCTs from
|
||||
// these logs will be extracted and verified; other SCTs will be treated as
|
||||
// unrecognized.
|
||||
[EnableIf=is_ct_supported]
|
||||
array<CTLogInfo> ct_logs;
|
||||
|
||||
// The initial CT policy to be used for requests. See
|
||||
// NetworkContext.SetCTPolicy() for more.
|
||||
[EnableIf=is_ct_supported]
|
||||
CTPolicy? ct_policy;
|
||||
|
||||
// When the Certificate Transparency logs in |ct_logs| were last updated. If
|
||||
// When the built-in Certificate Transparency logs were last updated. If
|
||||
// |enforce_chrome_ct_policy| is set, and |ct_log_update_time| is not
|
||||
// sufficiently recent, enforcement of the "Certificate Transparency in
|
||||
// Chrome" policy will be disabled.
|
||||
|
@ -38,6 +38,9 @@ import "url/mojom/url.mojom";
|
||||
[EnableIf=is_android]
|
||||
import "mojo/public/mojom/base/application_state.mojom";
|
||||
|
||||
[EnableIf=is_ct_supported]
|
||||
import "services/network/public/mojom/ct_log_info.mojom";
|
||||
|
||||
// Values for configuring HTTP authentication that can only be set once.
|
||||
struct HttpAuthStaticParams {
|
||||
// List of supported auth schemes. Unrecognized schemes are ignored.
|
||||
@ -338,6 +341,15 @@ interface NetworkService {
|
||||
MutableNetworkTrafficAnnotationTag traffic_annotation,
|
||||
pending_remote<network.mojom.URLLoaderFactory> factory);
|
||||
|
||||
|
||||
// Updates the log list used for CT verification.
|
||||
[EnableIf=is_ct_supported]
|
||||
UpdateCtLogList(array<CTLogInfo> log_list);
|
||||
|
||||
// Disables or enables CT Enforcement.
|
||||
[EnableIf=is_ct_supported]
|
||||
SetCtEnforcementEnabled(bool enabled);
|
||||
|
||||
// Calls base::debug::DumpWithoutCrashing for the network process.
|
||||
// TODO(http://crbug.com/934317): Remove this once done debugging renderer
|
||||
// hangs.
|
||||
|
Reference in New Issue
Block a user