[go: nahoru, domu]

blob: ba7f85b849fee37e97d5f2baf2548c5c09ccf157 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/network/sct_auditing/sct_auditing_cache.h"
#include <algorithm>
#include "base/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 {
// Records the high-water mark of the cache size (in number of reports).
void RecordSCTAuditingCacheHighWaterMarkMetrics(size_t cache_hwm) {
base::UmaHistogramCounts1000("Security.SCTAuditing.OptIn.DedupeCacheHWM",
cache_hwm);
}
// Records whether a new report is deduplicated against an existing report in
// the cache.
void RecordSCTAuditingReportDeduplicatedMetrics(bool deduplicated) {
base::UmaHistogramBoolean("Security.SCTAuditing.OptIn.ReportDeduplicated",
deduplicated);
}
// Records whether a new report that wasn't deduplicated was sampled for
// sending to the reporting server.
void RecordSCTAuditingReportSampledMetrics(bool sampled) {
base::UmaHistogramBoolean("Security.SCTAuditing.OptIn.ReportSampled",
sampled);
}
// Records the size of a report that will be sent to the reporting server, in
// bytes. Used to track how much bandwidth is consumed by sending reports.
void RecordSCTAuditingReportSizeMetrics(size_t report_size) {
base::UmaHistogramCounts10M("Security.SCTAuditing.OptIn.ReportSize",
report_size);
}
} // namespace
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()) {
// High-water-mark metrics get logged hourly (rather than once-per-session
// at shutdown, as Network Service shutdown is not consistent and
// non-browser processes can fail to report metrics during shutdown).
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();
// Encode the SCTs in the report and generate the cache key. The hash of the
// SCTs is used as the cache key to deduplicate reports with the same SCTs.
// Constructing the report in parallel with computing the hash avoids
// encoding the SCTs multiple times and avoids extra copies.
SHA256_CTX ctx;
SHA256_Init(&ctx);
for (const auto& sct : signed_certificate_timestamps) {
auto* sct_source_and_status = tls_report->add_included_sct();
// TODO(crbug.com/1082860): Update the proto to remove the status entirely
// since only valid SCTs are reported now.
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());
}
// Don't handle reports if there were no valid SCTs.
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);
// Check if the SCTs are already in the cache. This will update the last seen
// time if they are present in the cache.
auto it = dedupe_cache_.Get(cache_key);
if (it != dedupe_cache_.end()) {
RecordSCTAuditingReportDeduplicatedMetrics(true);
return absl::nullopt;
}
RecordSCTAuditingReportDeduplicatedMetrics(false);
report->set_user_agent(version_info::GetProductNameAndVersionForUserAgent());
// Add `cache_key` to the dedupe cache. The cache value is not used.
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());
// Convert the certificate chain to a PEM encoded vector, and then initialize
// the proto's |certificate_chain| repeated field using the data in the
// vector. Note that GetPEMEncodedChain() can fail, but we still want to
// enqueue the report for the SCTs (in that case, |certificate_chain| is not
// guaranteed to be valid).
std::vector<std::string> certificate_chain;
validated_certificate_chain->GetPEMEncodedChain(&certificate_chain);
*connection_context->mutable_certificate_chain() = {certificate_chain.begin(),
certificate_chain.end()};
// Log the size of the report. This only tracks reports that are not dropped
// due to sampling (as those reports will just be empty).
RecordSCTAuditingReportSizeMetrics(report->ByteSizeLong());
// Track high-water-mark for the size of the cache.
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) {
// Copy into a vector to make comparisons easier.
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() {
// Empty the deduplication cache.
dedupe_cache_.Clear();
}
void SCTAuditingCache::ReportHWMMetrics() {
RecordSCTAuditingCacheHighWaterMarkMetrics(dedupe_cache_size_hwm_);
}
} // namespace network