#include "services/network/sct_auditing/sct_auditing_cache.h"
#include <algorithm>
#include "base/functional/callback.h"
#include "base/metrics/histogram_functions.h"
#include "base/rand_util.h"
#include "base/time/time.h"
#include "components/version_info/version_info.h"
#include "crypto/secure_hash.h"
#include "crypto/sha2.h"
#include "net/base/hash_value.h"
#include "net/cert/ct_serialization.h"
#include "net/cert/sct_status_flags.h"
#include "net/cert/signed_certificate_timestamp.h"
#include "net/cert/signed_certificate_timestamp_and_status.h"
#include "net/cert/x509_certificate.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "services/network/network_context.h"
#include "services/network/public/proto/sct_audit_report.pb.h"
#include "services/network/sct_auditing/sct_auditing_handler.h"
#include "third_party/boringssl/src/include/openssl/pool.h"
#include "third_party/boringssl/src/include/openssl/sha.h"
namespace network {
namespace {
void RecordSCTAuditingCacheHighWaterMarkMetrics(size_t cache_hwm) {
base::UmaHistogramCounts1000("Security.SCTAuditing.OptIn.DedupeCacheHWM",
cache_hwm);
}
void RecordSCTAuditingReportDeduplicatedMetrics(bool deduplicated) {
base::UmaHistogramBoolean("Security.SCTAuditing.OptIn.ReportDeduplicated",
deduplicated);
}
void RecordSCTAuditingReportSampledMetrics(bool sampled) {
base::UmaHistogramBoolean("Security.SCTAuditing.OptIn.ReportSampled",
sampled);
}
void RecordSCTAuditingReportSizeMetrics(size_t report_size) {
base::UmaHistogramCounts10M("Security.SCTAuditing.OptIn.ReportSize",
report_size);
}
}
SCTAuditingCache::ReportEntry::ReportEntry() = default;
SCTAuditingCache::ReportEntry::ReportEntry(ReportEntry&& other) = default;
SCTAuditingCache::ReportEntry::~ReportEntry() = default;
SCTAuditingCache::SCTAuditingCache(size_t cache_size)
: dedupe_cache_(cache_size) {}
SCTAuditingCache::~SCTAuditingCache() = default;
void SCTAuditingCache::Configure(
mojom::SCTAuditingConfigurationPtr configuration) {
configuration_ = std::move(configuration);
}
mojom::SCTAuditingConfigurationPtr SCTAuditingCache::GetConfiguration() const {
return configuration_.Clone();
}
absl::optional<SCTAuditingCache::ReportEntry>
SCTAuditingCache::MaybeGenerateReportEntry(
const net::HostPortPair& host_port_pair,
const net::X509Certificate* validated_certificate_chain,
const net::SignedCertificateTimestampAndStatusList&
signed_certificate_timestamps) {
if (!configuration_) {
return absl::nullopt;
}
if (!histogram_timer_.IsRunning()) {
histogram_timer_.Start(FROM_HERE, base::Hours(1), this,
&SCTAuditingCache::ReportHWMMetrics);
}
auto report = std::make_unique<sct_auditing::SCTClientReport>();
auto* tls_report = report->add_certificate_report();
SHA256_CTX ctx;
SHA256_Init(&ctx);
for (const auto& sct : signed_certificate_timestamps) {
auto* sct_source_and_status = tls_report->add_included_sct();
sct_source_and_status->set_status(
sct_auditing::SCTWithVerifyStatus::SctVerifyStatus::
SCTWithVerifyStatus_SctVerifyStatus_OK);
net::ct::EncodeSignedCertificateTimestamp(
sct.sct, sct_source_and_status->mutable_serialized_sct());
SHA256_Update(&ctx, sct_source_and_status->serialized_sct().data(),
sct_source_and_status->serialized_sct().size());
}
if (tls_report->included_sct().empty())
return absl::nullopt;
net::HashValue cache_key(net::HASH_VALUE_SHA256);
SHA256_Final(reinterpret_cast<uint8_t*>(cache_key.data()), &ctx);
auto it = dedupe_cache_.Get(cache_key);
if (it != dedupe_cache_.end()) {
RecordSCTAuditingReportDeduplicatedMetrics(true);
return absl::nullopt;
}
RecordSCTAuditingReportDeduplicatedMetrics(false);
report->set_user_agent(
std::string(version_info::GetProductNameAndVersionForUserAgent()));
dedupe_cache_.Put(cache_key, true);
if (base::RandDouble() > configuration_->sampling_rate) {
RecordSCTAuditingReportSampledMetrics(false);
return absl::nullopt;
}
RecordSCTAuditingReportSampledMetrics(true);
auto* connection_context = tls_report->mutable_context();
base::TimeDelta time_since_unix_epoch =
base::Time::Now() - base::Time::UnixEpoch();
connection_context->set_time_seen(time_since_unix_epoch.InSeconds());
auto* origin = connection_context->mutable_origin();
origin->set_hostname(host_port_pair.host());
origin->set_port(host_port_pair.port());
std::vector<std::string> certificate_chain;
validated_certificate_chain->GetPEMEncodedChain(&certificate_chain);
*connection_context->mutable_certificate_chain() = {certificate_chain.begin(),
certificate_chain.end()};
RecordSCTAuditingReportSizeMetrics(report->ByteSizeLong());
if (dedupe_cache_.size() > dedupe_cache_size_hwm_)
dedupe_cache_size_hwm_ = dedupe_cache_.size();
ReportEntry report_entry;
report_entry.key = std::move(cache_key);
report_entry.report = std::move(report);
return report_entry;
}
bool SCTAuditingCache::IsPopularSCT(base::span<const uint8_t> sct_leaf_hash) {
std::vector<uint8_t> leaf_hash(sct_leaf_hash.begin(), sct_leaf_hash.end());
return std::binary_search(popular_scts_.begin(), popular_scts_.end(),
leaf_hash);
}
void SCTAuditingCache::ClearCache() {
dedupe_cache_.Clear();
}
void SCTAuditingCache::ReportHWMMetrics() {
RecordSCTAuditingCacheHighWaterMarkMetrics(dedupe_cache_size_hwm_);
}
}