blob: 45efab95bcb322d29d4f6a86d1ae2783800cfdb0 [file] [log] [blame]
// Copyright 2016 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/safe_browsing/certificate_reporting_service_test_utils.h"
#include "base/strings/string_piece.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/ssl/certificate_error_report.h"
#include "components/encrypted_messages/encrypted_message.pb.h"
#include "components/encrypted_messages/message_encrypter.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_utils.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/base/upload_data_stream.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request_filter.h"
#include "services/network/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/boringssl/src/include/openssl/curve25519.h"
namespace {
static const char kHkdfLabel[] = "certificate report";
const uint32_t kServerPublicKeyTestVersion = 16;
std::string GetReportContents(const network::ResourceRequest& request,
const uint8_t* server_private_key) {
std::string serialized_report(network::GetUploadData(request));
encrypted_messages::EncryptedMessage encrypted_message;
EXPECT_TRUE(encrypted_message.ParseFromString(serialized_report));
EXPECT_EQ(kServerPublicKeyTestVersion,
encrypted_message.server_public_key_version());
EXPECT_EQ(
encrypted_messages::EncryptedMessage::AEAD_ECDH_AES_128_CTR_HMAC_SHA256,
encrypted_message.algorithm());
std::string decrypted_report;
// TODO(estark): kHkdfLabel needs to include the null character in the label
// due to a matching error in the server for the case of certificate
// reporting, the strlen + 1 can be removed once that error is fixed.
// https://crbug.com/517746
EXPECT_TRUE(encrypted_messages::DecryptMessageForTesting(
server_private_key, base::StringPiece(kHkdfLabel, strlen(kHkdfLabel) + 1),
encrypted_message, &decrypted_report));
return decrypted_report;
}
void WaitReports(
certificate_reporting_test_utils::RequestObserver* observer,
const certificate_reporting_test_utils::ReportExpectation& expectation,
std::vector<std::string>* full_reports) {
observer->Wait(expectation.num_reports());
EXPECT_EQ(expectation.successful_reports, observer->successful_reports());
EXPECT_EQ(expectation.failed_reports, observer->failed_reports());
EXPECT_EQ(expectation.delayed_reports, observer->delayed_reports());
if (full_reports)
*full_reports = observer->full_reports();
observer->ClearObservedReports();
}
} // namespace
namespace certificate_reporting_test_utils {
RequestObserver::RequestObserver()
: num_events_to_wait_for_(0u), num_received_events_(0u) {}
RequestObserver::~RequestObserver() {}
void RequestObserver::Wait(unsigned int num_events_to_wait_for) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!run_loop_);
ASSERT_LE(num_received_events_, num_events_to_wait_for)
<< "Observed unexpected report";
if (num_received_events_ < num_events_to_wait_for) {
num_events_to_wait_for_ = num_events_to_wait_for;
run_loop_.reset(new base::RunLoop());
run_loop_->Run();
run_loop_.reset(nullptr);
EXPECT_EQ(0u, num_received_events_);
EXPECT_EQ(0u, num_events_to_wait_for_);
} else if (num_received_events_ == num_events_to_wait_for) {
num_received_events_ = 0u;
num_events_to_wait_for_ = 0u;
}
}
void RequestObserver::OnRequest(const std::string& serialized_report,
ReportSendingResult report_type) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
CertificateErrorReport report;
EXPECT_TRUE(report.InitializeFromString(serialized_report));
full_reports_.push_back(serialized_report);
switch (report_type) {
case REPORTS_SUCCESSFUL:
successful_reports_[report.hostname()] =
report.is_retry_upload() ? RETRIED : NOT_RETRIED;
break;
case REPORTS_FAIL:
failed_reports_[report.hostname()] =
report.is_retry_upload() ? RETRIED : NOT_RETRIED;
break;
case REPORTS_DELAY:
delayed_reports_[report.hostname()] =
report.is_retry_upload() ? RETRIED : NOT_RETRIED;
break;
}
num_received_events_++;
if (!run_loop_) {
return;
}
ASSERT_LE(num_received_events_, num_events_to_wait_for_)
<< "Observed unexpected report";
if (num_received_events_ == num_events_to_wait_for_) {
num_events_to_wait_for_ = 0u;
num_received_events_ = 0u;
run_loop_->Quit();
}
}
const ObservedReportMap& RequestObserver::successful_reports() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return successful_reports_;
}
const ObservedReportMap& RequestObserver::failed_reports() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return failed_reports_;
}
const ObservedReportMap& RequestObserver::delayed_reports() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return delayed_reports_;
}
const std::vector<std::string>& RequestObserver::full_reports() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return full_reports_;
}
void RequestObserver::ClearObservedReports() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
successful_reports_.clear();
failed_reports_.clear();
delayed_reports_.clear();
full_reports_.clear();
}
DelayableCertReportURLRequestJob::DelayableCertReportURLRequestJob(
bool delayed,
bool should_fail,
net::URLRequest* request,
net::NetworkDelegate* network_delegate,
const base::Callback<void()>& destruction_callback)
: net::URLRequestJob(request, network_delegate),
delayed_(delayed),
should_fail_(should_fail),
started_(false),
destruction_callback_(destruction_callback),
weak_factory_(this) {}
DelayableCertReportURLRequestJob::~DelayableCertReportURLRequestJob() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
destruction_callback_);
}
base::WeakPtr<DelayableCertReportURLRequestJob>
DelayableCertReportURLRequestJob::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void DelayableCertReportURLRequestJob::Start() {
started_ = true;
if (delayed_) {
// Do nothing until Resume() is called.
return;
}
Resume();
}
int DelayableCertReportURLRequestJob::ReadRawData(net::IOBuffer* buf,
int buf_size) {
// Report sender ignores responses. Return empty response.
return 0;
}
void DelayableCertReportURLRequestJob::GetResponseInfo(
net::HttpResponseInfo* info) {
// Report sender ignores responses. Return empty response.
if (!should_fail_) {
info->headers = new net::HttpResponseHeaders("HTTP/1.1 200 OK");
}
}
void DelayableCertReportURLRequestJob::Resume() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!started_) {
// If Start() hasn't been called yet, then unset |delayed_| so that when
// Start() is called, the request will begin immediately.
delayed_ = false;
return;
}
if (should_fail_) {
NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
net::ERR_SSL_PROTOCOL_ERROR));
return;
}
// Start reading asynchronously as would a normal network request.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&DelayableCertReportURLRequestJob::NotifyHeadersComplete,
weak_factory_.GetWeakPtr()));
}
ReportExpectation::ReportExpectation() {}
ReportExpectation::ReportExpectation(const ReportExpectation& other) = default;
ReportExpectation::~ReportExpectation() {}
// static
ReportExpectation ReportExpectation::Successful(
const ObservedReportMap& reports) {
ReportExpectation expectation;
expectation.successful_reports = reports;
return expectation;
}
// static
ReportExpectation ReportExpectation::Failed(const ObservedReportMap& reports) {
ReportExpectation expectation;
expectation.failed_reports = reports;
return expectation;
}
// static
ReportExpectation ReportExpectation::Delayed(const ObservedReportMap& reports) {
ReportExpectation expectation;
expectation.delayed_reports = reports;
return expectation;
}
int ReportExpectation::num_reports() const {
return successful_reports.size() + failed_reports.size() +
delayed_reports.size();
}
CertificateReportingServiceObserver::CertificateReportingServiceObserver() {}
CertificateReportingServiceObserver::~CertificateReportingServiceObserver() {}
void CertificateReportingServiceObserver::Clear() {
did_reset_ = false;
}
void CertificateReportingServiceObserver::WaitForReset() {
DCHECK(!run_loop_);
if (did_reset_)
return;
run_loop_.reset(new base::RunLoop());
run_loop_->Run();
run_loop_.reset();
}
void CertificateReportingServiceObserver::OnServiceReset() {
did_reset_ = true;
if (run_loop_)
run_loop_->Quit();
}
CertificateReportingServiceTestHelper::CertificateReportingServiceTestHelper()
: expected_report_result_(REPORTS_FAIL) {
memset(server_private_key_, 1, sizeof(server_private_key_));
X25519_public_from_private(server_public_key_, server_private_key_);
}
CertificateReportingServiceTestHelper::
~CertificateReportingServiceTestHelper() {}
void CertificateReportingServiceTestHelper::SetFailureMode(
ReportSendingResult expected_report_result) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
expected_report_result_ = expected_report_result;
}
void CertificateReportingServiceTestHelper::ResumeDelayedRequest() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
EXPECT_EQ(REPORTS_DELAY, expected_report_result_);
if (delayed_client_) {
SendResponse(std::move(delayed_client_), delayed_result_ == REPORTS_FAIL);
request_destroyed_observer_.OnRequest(delayed_report_, delayed_result_);
}
}
uint8_t* CertificateReportingServiceTestHelper::server_public_key() {
return server_public_key_;
}
uint32_t CertificateReportingServiceTestHelper::server_public_key_version()
const {
return kServerPublicKeyTestVersion;
}
void CertificateReportingServiceTestHelper::WaitForRequestsCreated(
const ReportExpectation& expectation) {
WaitReports(&request_created_observer_, expectation, nullptr);
}
void CertificateReportingServiceTestHelper::WaitForRequestsCreated(
const ReportExpectation& expectation,
std::vector<std::string>* full_reports) {
WaitReports(&request_created_observer_, expectation, full_reports);
}
void CertificateReportingServiceTestHelper::WaitForRequestsDestroyed(
const ReportExpectation& expectation) {
WaitReports(&request_destroyed_observer_, expectation, nullptr);
}
void CertificateReportingServiceTestHelper::WaitForRequestsDestroyed(
const ReportExpectation& expectation,
std::vector<std::string>* full_reports) {
WaitReports(&request_destroyed_observer_, expectation, full_reports);
}
void CertificateReportingServiceTestHelper::ExpectNoRequests(
CertificateReportingService* service) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Check that all requests have been destroyed.
EXPECT_TRUE(request_destroyed_observer_.successful_reports().empty());
EXPECT_TRUE(request_destroyed_observer_.failed_reports().empty());
EXPECT_TRUE(request_destroyed_observer_.delayed_reports().empty());
if (service->GetReporterForTesting()) {
// Reporter can be null if reporting is disabled.
EXPECT_EQ(
0u,
service->GetReporterForTesting()->inflight_report_count_for_testing());
}
}
void CertificateReportingServiceTestHelper::SendResponse(
network::mojom::URLLoaderClientPtr client,
bool fail) {
if (fail) {
client->OnComplete(
network::URLLoaderCompletionStatus(net::ERR_SSL_PROTOCOL_ERROR));
return;
}
network::ResourceResponseHead head;
head.headers = new net::HttpResponseHeaders(
"HTTP/1.1 200 OK\nContent-type: text/html\n\n");
head.mime_type = "text/html";
client->OnReceiveResponse(head);
client->OnComplete(network::URLLoaderCompletionStatus());
}
void CertificateReportingServiceTestHelper::CreateLoaderAndStart(
network::mojom::URLLoaderRequest request,
int32_t routing_id,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& url_request,
network::mojom::URLLoaderClientPtr client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
const std::string serialized_report =
GetReportContents(url_request, server_private_key_);
request_created_observer_.OnRequest(serialized_report,
expected_report_result_);
if (expected_report_result_ == REPORTS_FAIL) {
SendResponse(std::move(client), true);
request_destroyed_observer_.OnRequest(serialized_report,
expected_report_result_);
return;
}
if (expected_report_result_ == REPORTS_DELAY) {
DCHECK(!delayed_client_) << "Supports only one delayed request at a time";
delayed_client_ = std::move(client);
delayed_report_ = serialized_report;
delayed_result_ = expected_report_result_;
return;
}
SendResponse(std::move(client), false);
request_destroyed_observer_.OnRequest(serialized_report,
expected_report_result_);
}
void CertificateReportingServiceTestHelper::Clone(
network::mojom::URLLoaderFactoryRequest request) {
NOTREACHED();
}
std::unique_ptr<network::SharedURLLoaderFactoryInfo>
CertificateReportingServiceTestHelper::Clone() {
NOTREACHED();
return nullptr;
}
EventHistogramTester::EventHistogramTester() {}
EventHistogramTester::~EventHistogramTester() {
if (submitted_) {
histogram_tester_.ExpectBucketCount(
CertificateReportingService::kReportEventHistogram,
static_cast<int>(CertificateReportingService::ReportOutcome::SUBMITTED),
submitted_);
}
if (failed_) {
histogram_tester_.ExpectBucketCount(
CertificateReportingService::kReportEventHistogram,
static_cast<int>(CertificateReportingService::ReportOutcome::FAILED),
failed_);
}
if (successful_) {
histogram_tester_.ExpectBucketCount(
CertificateReportingService::kReportEventHistogram,
static_cast<int>(
CertificateReportingService::ReportOutcome::SUCCESSFUL),
successful_);
}
if (dropped_) {
histogram_tester_.ExpectBucketCount(
CertificateReportingService::kReportEventHistogram,
static_cast<int>(
CertificateReportingService::ReportOutcome::DROPPED_OR_IGNORED),
dropped_);
}
histogram_tester_.ExpectTotalCount(
CertificateReportingService::kReportEventHistogram,
submitted_ + failed_ + successful_ + dropped_);
}
void EventHistogramTester::SetExpectedValues(int submitted,
int failed,
int successful,
int dropped) {
submitted_ = submitted;
failed_ = failed;
successful_ = successful;
dropped_ = dropped;
}
} // namespace certificate_reporting_test_utils