blob: 5158b3ab2aba3cad9be846ee64ceaec38678b708 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/network/legacy_tls_config_distributor.h"
#include <memory>
#include <string>
#include "base/containers/span.h"
#include "base/location.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "crypto/sha2.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
namespace network {
namespace {
// Helper to guarantee |notify_callback| is run, even if |process_callback|
// no-ops due to the worker pool doing the parsing outliving the
// LegacyTLSConfigDistributor.
void ProcessParsedLegacyTLSConfig(
base::OnceCallback<void(scoped_refptr<LegacyTLSExperimentConfig>)>
process_callback,
base::OnceClosure notify_callback,
scoped_refptr<LegacyTLSExperimentConfig> config) {
std::move(process_callback).Run(std::move(config));
std::move(notify_callback).Run();
}
} // namespace
LegacyTLSExperimentConfig::LegacyTLSExperimentConfig() = default;
LegacyTLSExperimentConfig::~LegacyTLSExperimentConfig() = default;
// static
scoped_refptr<LegacyTLSExperimentConfig> LegacyTLSExperimentConfig::Parse(
const std::string& data) {
auto config = base::MakeRefCounted<LegacyTLSExperimentConfig>();
if (data.empty() || !config->proto_.ParseFromString(data))
return nullptr;
return config;
}
bool LegacyTLSExperimentConfig::ShouldSuppressLegacyTLSWarning(
const std::string& hostname) const {
// Match on eTLD+1 rather than full hostname (to account for subdomains and
// redirects). If no registrable domain is found, default to using the
// hostname as-is.
auto domain = net::registry_controlled_domains::GetDomainAndRegistry(
hostname, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
if (domain.empty())
domain = hostname;
// Convert bytes from crypto::SHA256 so we can compare to the proto contents.
std::string host_hash_bytes = crypto::SHA256HashString(domain);
std::string host_hash = base::ToLowerASCII(
base::HexEncode(host_hash_bytes.data(), host_hash_bytes.size()));
const auto& control_site_hashes = proto_.control_site_hashes();
// Perform binary search on the sorted list of control site hashes to check
// if the input URL's hostname is included.
auto lower = std::lower_bound(control_site_hashes.begin(),
control_site_hashes.end(), host_hash);
return lower != control_site_hashes.end() && *lower == host_hash;
}
LegacyTLSConfigDistributor::LegacyTLSConfigDistributor() = default;
LegacyTLSConfigDistributor::~LegacyTLSConfigDistributor() = default;
void LegacyTLSConfigDistributor::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void LegacyTLSConfigDistributor::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void LegacyTLSConfigDistributor::OnNewLegacyTLSConfig(
base::span<const uint8_t> config,
base::OnceClosure callback) {
// Make a copy for the background task, since the underlying storage for the
// span will go away.
std::string config_string(reinterpret_cast<const char*>(config.data()),
config.size());
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::TaskPriority::USER_VISIBLE},
base::BindOnce(&LegacyTLSExperimentConfig::Parse,
std::move(config_string)),
base::BindOnce(
&ProcessParsedLegacyTLSConfig,
base::BindOnce(&LegacyTLSConfigDistributor::OnLegacyTLSConfigParsed,
weak_factory_.GetWeakPtr()),
std::move(callback)));
}
void LegacyTLSConfigDistributor::OnLegacyTLSConfigParsed(
scoped_refptr<LegacyTLSExperimentConfig> config) {
if (!config)
return; // Error parsing the config.
config_ = std::move(config);
for (auto& observer : observers_) {
observer.OnNewLegacyTLSConfig(config_);
}
}
} // namespace network