blob: cf85d79879a5be480c0522a85eb25e6ea3295272 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/metrics/private_metrics/data_upload_config_downloader.h"
#include <memory>
#include "base/metrics/histogram_functions.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "url/gurl.h"
namespace metrics::private_metrics {
namespace {
constexpr size_t kMaxDownloadBytes = 4 * 1024 * 1024; // 4 MiBs
// Max number of retries for fetching the configuration.
constexpr int kMaxRetries = 3;
// The conditions for which a retry will trigger.
constexpr int kRetryMode = network::SimpleURLLoader::RETRY_ON_5XX |
network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE |
network::SimpleURLLoader::RETRY_ON_NAME_NOT_RESOLVED;
constexpr net::NetworkTrafficAnnotationTag kPrivateMetricsKeyNetworkTag =
net::DefineNetworkTrafficAnnotation("private_metrics_encryption_key", R"(
semantics {
sender: "Private Metrics Encryption Key"
description:
"Retrieves a public key and its signed endorsements in the form "
"of a serialized DataUploadConfig. The public key will be used to "
"encrypt PrivateMetricReport prior to uploading to Google "
"servers. The private key will only be granted in a trusted "
"execution environment, and the signed endorsements can be used "
"to verify the attestation."
trigger:
"Private Metric encryption keys will automatically be fetched "
"while Chrome is running with usage statistics and 'Make searches "
"and browsing better' settings enabled."
data: "None"
user_data {
type: NONE
}
internal {
contacts {
email: "//components/metrics/OWNERS"
}
}
destination: GOOGLE_OWNED_SERVICE
last_reviewed: "2025-08-21"
}
policy {
cookies_allowed: NO
setting:
"Users can enable or disable this feature by disabling 'Make "
"searches and browsing better' in Chrome's settings under Advanced "
"Settings, Privacy. This has to be enabled for all active "
"profiles. This is only enabled if the user has 'Help improve "
"Chrome's features and performance' enabled in the same settings "
"menu."
chrome_policy {
MetricsReportingEnabled {
policy_options {mode: MANDATORY}
MetricsReportingEnabled: false
}
UrlKeyedAnonymizedDataCollectionEnabled {
policy_options {mode: MANDATORY}
UrlKeyedAnonymizedDataCollectionEnabled: false
}
}
})");
inline constexpr char kDataUploadConfigGstaticUrl[] =
"https://www.gstatic.com/chrome/private-metrics/data-upload-config";
} // namespace
std::unique_ptr<network::SimpleURLLoader> CreateSimpleURLLoader(
const GURL& url) {
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = url;
resource_request->method = "GET";
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
return network::SimpleURLLoader::Create(std::move(resource_request),
kPrivateMetricsKeyNetworkTag);
}
DataUploadConfigDownloader::DataUploadConfigDownloader(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: url_loader_factory_(url_loader_factory) {}
DataUploadConfigDownloader::~DataUploadConfigDownloader() = default;
void DataUploadConfigDownloader::FetchDataUploadConfig(
DataUploadConfigCallback callback) {
if (!url_loader_factory_ || pending_request_) {
return;
}
GURL destination = GURL(kDataUploadConfigGstaticUrl);
pending_request_ = CreateSimpleURLLoader(destination);
network::SimpleURLLoader::BodyAsStringCallback handler = base::BindOnce(
&DataUploadConfigDownloader::HandleSerializedDataUploadConfig,
self_ptr_factory_.GetWeakPtr(), std::move(callback));
pending_request_->SetRetryOptions(kMaxRetries, kRetryMode);
pending_request_->DownloadToString(url_loader_factory_.get(),
std::move(handler), kMaxDownloadBytes);
}
raw_ptr<network::SimpleURLLoader>
DataUploadConfigDownloader::GetPendingRequestForTesting() {
return pending_request_.get();
}
void DataUploadConfigDownloader::HandleSerializedDataUploadConfig(
DataUploadConfigCallback callback,
std::optional<std::string> response_body) {
if (!pending_request_) {
return;
}
// Move the pending request here so it's deleted when this function ends.
std::unique_ptr<network::SimpleURLLoader> request =
std::move(pending_request_);
// Response code is not 200 or `response_body` is empty.
if (!request->ResponseInfo() || !request->ResponseInfo()->headers ||
request->ResponseInfo()->headers->response_code() != net::HTTP_OK ||
!response_body.has_value() || response_body->empty()) {
std::move(callback).Run(std::nullopt);
return;
}
base::UmaHistogramCounts10000("PrivateMetrics.DataUploadConfig.DownloadSize",
response_body->size() / 1024);
fcp::confidentialcompute::DataUploadConfig data_upload_config;
if (!data_upload_config.ParseFromString(response_body.value())) {
std::move(callback).Run(std::nullopt);
return;
}
std::move(callback).Run(data_upload_config);
}
} // namespace metrics::private_metrics