blob: 18ef459f2df8c8cf0e7acbf32d35c56efa1c140e [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 "components/certificate_reporting/error_report.h"
#include <set>
#include <string>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "components/certificate_reporting/cert_logger.pb.h"
#include "net/base/test_data_directory.h"
#include "net/cert/cert_status_flags.h"
#include "net/ssl/ssl_info.h"
#include "net/test/cert_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using net::SSLInfo;
using testing::UnorderedElementsAre;
namespace certificate_reporting {
namespace {
const char kDummyHostname[] = "dummy.hostname.com";
const char kDummyFailureLog[] = "dummy failure log";
const char kTestCertFilename[] = "test_mail_google_com.pem";
const net::CertStatus kCertStatus =
net::CERT_STATUS_COMMON_NAME_INVALID | net::CERT_STATUS_REVOKED;
const CertLoggerRequest::CertError kFirstReportedCertError =
CertLoggerRequest::ERR_CERT_COMMON_NAME_INVALID;
const CertLoggerRequest::CertError kSecondReportedCertError =
CertLoggerRequest::ERR_CERT_REVOKED;
// Whether to include an unverified certificate chain in the test
// SSLInfo. In production code, an unverified cert chain will not be
// present if the resource was loaded from cache.
enum UnverifiedCertChainStatus {
INCLUDE_UNVERIFIED_CERT_CHAIN,
EXCLUDE_UNVERIFIED_CERT_CHAIN
};
void GetTestSSLInfo(UnverifiedCertChainStatus unverified_cert_chain_status,
SSLInfo* info) {
info->cert =
net::ImportCertFromFile(net::GetTestCertsDirectory(), kTestCertFilename);
ASSERT_TRUE(info->cert);
if (unverified_cert_chain_status == INCLUDE_UNVERIFIED_CERT_CHAIN) {
info->unverified_cert = net::ImportCertFromFile(
net::GetTestCertsDirectory(), kTestCertFilename);
ASSERT_TRUE(info->unverified_cert);
}
info->is_issued_by_known_root = true;
info->cert_status = kCertStatus;
info->pinning_failure_log = kDummyFailureLog;
}
std::string GetPEMEncodedChain() {
base::FilePath cert_path =
net::GetTestCertsDirectory().AppendASCII(kTestCertFilename);
std::string cert_data;
EXPECT_TRUE(base::ReadFileToString(cert_path, &cert_data));
return cert_data;
}
void VerifyErrorReportSerialization(const ErrorReport& report,
const SSLInfo& ssl_info) {
std::string serialized_report;
ASSERT_TRUE(report.Serialize(&serialized_report));
CertLoggerRequest deserialized_report;
ASSERT_TRUE(deserialized_report.ParseFromString(serialized_report));
EXPECT_EQ(kDummyHostname, deserialized_report.hostname());
EXPECT_EQ(GetPEMEncodedChain(), deserialized_report.cert_chain());
EXPECT_EQ(GetPEMEncodedChain(), deserialized_report.unverified_cert_chain());
EXPECT_EQ(1, deserialized_report.pin().size());
EXPECT_EQ(kDummyFailureLog, deserialized_report.pin().Get(0));
EXPECT_EQ(
ssl_info.is_issued_by_known_root,
deserialized_report.is_issued_by_known_root());
EXPECT_THAT(
deserialized_report.cert_error(),
UnorderedElementsAre(kFirstReportedCertError, kSecondReportedCertError));
}
// Test that a serialized ErrorReport can be deserialized as
// a CertLoggerRequest protobuf (which is the format that the receiving
// server expects it in) with the right data in it.
TEST(ErrorReportTest, SerializedReportAsProtobuf) {
SSLInfo ssl_info;
ASSERT_NO_FATAL_FAILURE(
GetTestSSLInfo(INCLUDE_UNVERIFIED_CERT_CHAIN, &ssl_info));
ErrorReport report_known(kDummyHostname, ssl_info);
ASSERT_NO_FATAL_FAILURE(
VerifyErrorReportSerialization(report_known, ssl_info));
// Test that both values for |is_issued_by_known_root| are serialized
// correctly.
ssl_info.is_issued_by_known_root = false;
ErrorReport report_unknown(kDummyHostname, ssl_info);
ASSERT_NO_FATAL_FAILURE(
VerifyErrorReportSerialization(report_unknown, ssl_info));
}
TEST(ErrorReportTest, SerializedReportAsProtobufWithInterstitialInfo) {
std::string serialized_report;
SSLInfo ssl_info;
// Use EXCLUDE_UNVERIFIED_CERT_CHAIN here to exercise the code path
// where SSLInfo does not contain the unverified cert chain. (The test
// above exercises the path where it does.)
ASSERT_NO_FATAL_FAILURE(
GetTestSSLInfo(EXCLUDE_UNVERIFIED_CERT_CHAIN, &ssl_info));
ErrorReport report(kDummyHostname, ssl_info);
report.SetInterstitialInfo(ErrorReport::INTERSTITIAL_CLOCK,
ErrorReport::USER_PROCEEDED,
ErrorReport::INTERSTITIAL_OVERRIDABLE);
ASSERT_TRUE(report.Serialize(&serialized_report));
CertLoggerRequest deserialized_report;
ASSERT_TRUE(deserialized_report.ParseFromString(serialized_report));
EXPECT_EQ(kDummyHostname, deserialized_report.hostname());
EXPECT_EQ(GetPEMEncodedChain(), deserialized_report.cert_chain());
EXPECT_EQ(std::string(), deserialized_report.unverified_cert_chain());
EXPECT_EQ(1, deserialized_report.pin().size());
EXPECT_EQ(kDummyFailureLog, deserialized_report.pin().Get(0));
EXPECT_EQ(CertLoggerInterstitialInfo::INTERSTITIAL_CLOCK,
deserialized_report.interstitial_info().interstitial_reason());
EXPECT_EQ(true, deserialized_report.interstitial_info().user_proceeded());
EXPECT_EQ(true, deserialized_report.interstitial_info().overridable());
EXPECT_EQ(
ssl_info.is_issued_by_known_root,
deserialized_report.is_issued_by_known_root());
EXPECT_THAT(
deserialized_report.cert_error(),
UnorderedElementsAre(kFirstReportedCertError, kSecondReportedCertError));
}
// Test that a serialized report can be parsed.
TEST(ErrorReportTest, ParseSerializedReport) {
std::string serialized_report;
SSLInfo ssl_info;
ASSERT_NO_FATAL_FAILURE(
GetTestSSLInfo(INCLUDE_UNVERIFIED_CERT_CHAIN, &ssl_info));
ErrorReport report(kDummyHostname, ssl_info);
EXPECT_EQ(kDummyHostname, report.hostname());
ASSERT_TRUE(report.Serialize(&serialized_report));
ErrorReport parsed;
ASSERT_TRUE(parsed.InitializeFromString(serialized_report));
EXPECT_EQ(report.hostname(), parsed.hostname());
}
} // namespace
} // namespace certificate_reporting