blob: 948c1ae41852f07469d04d8522361fd2ab0c34a6 [file] [log] [blame]
// Copyright 2015 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/ssl/certificate_error_reporter.h"
#include <stdint.h>
#include <string.h>
#include <set>
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/string_piece.h"
#include "base/test/bind_test_util.h"
#include "components/encrypted_messages/encrypted_message.pb.h"
#include "components/encrypted_messages/message_encrypter.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.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 char kDummyHttpReportUri[] = "http://example.test";
const char kDummyHttpsReportUri[] = "https://example.test";
const char kDummyReport[] = "a dummy report";
const uint32_t kServerPublicKeyTestVersion = 16;
class ErrorReporterTest : public ::testing::Test {
public:
ErrorReporterTest()
: test_shared_loader_factory_(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_)) {
memset(server_private_key_, 1, sizeof(server_private_key_));
X25519_public_from_private(server_public_key_, server_private_key_);
}
~ErrorReporterTest() override {}
protected:
base::MessageLoopForIO loop_;
uint8_t server_public_key_[32];
uint8_t server_private_key_[32];
network::TestURLLoaderFactory test_url_loader_factory_;
scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
DISALLOW_COPY_AND_ASSIGN(ErrorReporterTest);
};
// Test that ErrorReporter::SendExtendedReportingReport sends
// an encrypted or plaintext extended reporting report as appropriate.
TEST_F(ErrorReporterTest, ExtendedReportingSendReport) {
GURL latest_report_uri;
std::string latest_report;
std::string latest_content_type;
test_url_loader_factory_.SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
latest_report_uri = request.url;
request.headers.GetHeader(net::HttpRequestHeaders::kContentType,
&latest_content_type);
latest_report = network::GetUploadData(request);
}));
// Data should not be encrypted when sent to an HTTPS URL.
const GURL https_url(kDummyHttpsReportUri);
CertificateErrorReporter https_reporter(test_shared_loader_factory_,
https_url, server_public_key_,
kServerPublicKeyTestVersion);
https_reporter.SendExtendedReportingReport(
kDummyReport, base::OnceCallback<void()>(),
base::OnceCallback<void(int, int)>());
EXPECT_EQ(latest_report_uri, https_url);
EXPECT_EQ(latest_report, kDummyReport);
// Data should be encrypted when sent to an HTTP URL.
const GURL http_url(kDummyHttpReportUri);
CertificateErrorReporter http_reporter(test_shared_loader_factory_, http_url,
server_public_key_,
kServerPublicKeyTestVersion);
http_reporter.SendExtendedReportingReport(
kDummyReport, base::OnceCallback<void()>(),
base::OnceCallback<void(int, int)>());
EXPECT_EQ(latest_report_uri, http_url);
EXPECT_EQ("application/octet-stream", latest_content_type);
std::string uploaded_report;
encrypted_messages::EncryptedMessage encrypted_report;
ASSERT_TRUE(encrypted_report.ParseFromString(latest_report));
EXPECT_EQ(kServerPublicKeyTestVersion,
encrypted_report.server_public_key_version());
EXPECT_EQ(
encrypted_messages::EncryptedMessage::AEAD_ECDH_AES_128_CTR_HMAC_SHA256,
encrypted_report.algorithm());
// 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
ASSERT_TRUE(encrypted_messages::DecryptMessageForTesting(
server_private_key_,
base::StringPiece(kHkdfLabel, strlen(kHkdfLabel) + 1), encrypted_report,
&uploaded_report));
EXPECT_EQ(kDummyReport, uploaded_report);
}
// Tests that an UMA histogram is recorded if a report fails to send.
TEST_F(ErrorReporterTest, ErroredRequestCallsCallback) {
base::RunLoop run_loop;
const GURL report_uri("http://foo.com/bar");
test_url_loader_factory_.AddResponse(
report_uri, network::ResourceResponseHead(), std::string(),
network::URLLoaderCompletionStatus(net::ERR_CONNECTION_FAILED));
CertificateErrorReporter reporter(test_shared_loader_factory_, report_uri);
reporter.SendExtendedReportingReport(
kDummyReport, base::BindLambdaForTesting([&]() { FAIL(); }),
base::BindLambdaForTesting(
[&](int net_error, int http_response_code) { run_loop.Quit(); }));
run_loop.Run();
}
// Tests that an UMA histogram is recorded if a report is successfully sent.
TEST_F(ErrorReporterTest, SuccessfulRequestCallsCallback) {
base::RunLoop run_loop;
const GURL report_uri("http://foo.com/bar");
test_url_loader_factory_.AddResponse(report_uri.spec(), "some data");
CertificateErrorReporter reporter(test_shared_loader_factory_, report_uri);
reporter.SendExtendedReportingReport(
kDummyReport, base::BindLambdaForTesting([&]() { run_loop.Quit(); }),
base::BindLambdaForTesting(
[&](int net_error, int http_response_code) { FAIL(); }));
run_loop.Run();
}
} // namespace