#include "net/cert/cert_verify_proc_builtin.h"
#include <algorithm>
#include <optional>
#include <string_view>
#include "base/containers/to_vector.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/numerics/safe_conversions.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_view_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "components/network_time/time_tracker/time_tracker.h"
#include "net/base/features.h"
#include "net/base/hash_value.h"
#include "net/base/ip_address.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/cert_verify_proc.h"
#include "net/cert/crl_set.h"
#include "net/cert/do_nothing_ct_verifier.h"
#include "net/cert/ev_root_ca_metadata.h"
#include "net/cert/internal/system_trust_store.h"
#include "net/cert/sct_status_flags.h"
#include "net/cert/time_conversions.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "net/cert_net/cert_net_fetcher_url_request.h"
#include "net/http/transport_security_state.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_with_source.h"
#include "net/log/test_net_log.h"
#include "net/test/cert_builder.h"
#include "net/test/cert_test_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "net/test/gtest_util.h"
#include "net/test/revocation_builder.h"
#include "net/test/two_qwac_cert_binding_builder.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/boringssl/src/pki/parse_certificate.h"
#include "third_party/boringssl/src/pki/trust_store.h"
#include "third_party/boringssl/src/pki/trust_store_collection.h"
#include "third_party/boringssl/src/pki/trust_store_in_memory.h"
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
#include "base/version_info/version_info.h"
#endif
using net::test::IsError;
using net::test::IsOk;
using testing::_;
namespace net {
namespace {
std::unique_ptr<test_server::HttpResponse> HangRequestAndCallback(
base::OnceClosure callback,
const test_server::HttpRequest& request) {
std::move(callback).Run();
return std::make_unique<test_server::HungResponse>();
}
void FailTest(const std::string& message) {
ADD_FAILURE() << message;
}
std::unique_ptr<test_server::HttpResponse> FailRequestAndFailTest(
const std::string& message,
scoped_refptr<base::TaskRunner> main_task_runner,
const test_server::HttpRequest& request) {
main_task_runner->PostTask(FROM_HERE, base::BindOnce(FailTest, message));
auto response = std::make_unique<test_server::BasicHttpResponse>();
response->set_code(HTTP_NOT_ACCEPTABLE);
return response;
}
std::unique_ptr<test_server::HttpResponse> ServeResponse(
HttpStatusCode status_code,
const std::string& content_type,
const std::string& content,
const test_server::HttpRequest& request) {
auto http_response = std::make_unique<test_server::BasicHttpResponse>();
http_response->set_code(status_code);
http_response->set_content_type(content_type);
http_response->set_content(content);
return http_response;
}
std::string MakeRandomHexString(size_t num_bytes) {
std::vector<uint8_t> rand_bytes(num_bytes);
base::RandBytes(rand_bytes);
return base::HexEncode(rand_bytes);
}
static std::string MakeRandomPath(std::string_view suffix) {
return "/" + MakeRandomHexString(12) + std::string(suffix);
}
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
std::vector<uint8_t> ParsePemCertificate(const std::string* pem_value) {
if (!pem_value) {
ADD_FAILURE() << "pem_value is null";
return {};
}
CertificateList certs = X509Certificate::CreateCertificateListFromBytes(
base::as_byte_span(*pem_value),
X509Certificate::Format::FORMAT_PEM_CERT_SEQUENCE);
if (certs.empty()) {
ADD_FAILURE() << "error decoding pem";
return {};
}
if (certs.size() > 1) {
ADD_FAILURE() << "multiple certs in pem";
return {};
}
return base::ToVector(certs[0]->cert_span());
}
std::vector<std::string> ParseNetLogCertificatesList(
const base::Value::List& list) {
std::vector<std::string> result;
for (const auto& pem_value : list) {
if (!pem_value.is_string()) {
result.push_back("Value is not a string");
continue;
}
CertificateList certs = X509Certificate::CreateCertificateListFromBytes(
base::as_byte_span(pem_value.GetString()),
X509Certificate::Format::FORMAT_PEM_CERT_SEQUENCE);
if (certs.empty()) {
result.push_back("error decoding pem");
continue;
}
if (certs.size() > 1) {
result.push_back("multiple certs in pem");
continue;
}
result.emplace_back(base::as_string_view(certs[0]->cert_span()));
}
return result;
}
std::vector<std::string> ParseNetLogCertificatesDict(
const base::Value::Dict& dict) {
auto* cert_list = dict.FindList("certificates");
if (!cert_list) {
ADD_FAILURE() << "no cerificates key in dict";
return {};
}
return ParseNetLogCertificatesList(*cert_list);
}
#endif
int VerifyOnWorkerThread(const scoped_refptr<CertVerifyProc>& verify_proc,
scoped_refptr<X509Certificate> cert,
const std::string& hostname,
const std::string& ocsp_response,
const std::string& sct_list,
int flags,
CertVerifyResult* verify_result,
NetLogSource* out_source) {
base::ScopedAllowBaseSyncPrimitivesForTesting scoped_allow_blocking;
NetLogWithSource net_log(NetLogWithSource::Make(
net::NetLog::Get(), net::NetLogSourceType::CERT_VERIFIER_TASK));
int error = verify_proc->Verify(cert.get(), hostname, ocsp_response, sct_list,
flags, verify_result, net_log);
*out_source = net_log.source();
return error;
}
class MockSystemTrustStore : public SystemTrustStore {
public:
bssl::TrustStore* GetTrustStore() override { return &trust_store_; }
bool IsKnownRoot(const bssl::ParsedCertificate* trust_anchor) const override {
return mock_is_known_root_;
}
void AddTrustStore(bssl::TrustStore* store) {
trust_store_.AddTrustStore(store);
}
void SetMockIsKnownRoot(bool is_known_root) {
mock_is_known_root_ = is_known_root;
}
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
net::PlatformTrustStore* GetPlatformTrustStore() override { return nullptr; }
void SetMockIsLocallyTrustedRoot(bool is_locally_trusted_root) {
mock_is_locally_trusted_root_ = is_locally_trusted_root;
}
bool IsLocallyTrustedRoot(
const bssl::ParsedCertificate* trust_anchor) override {
return mock_is_locally_trusted_root_;
}
int64_t chrome_root_store_version() const override { return 0; }
base::span<const ChromeRootCertConstraints> GetChromeRootConstraints(
const bssl::ParsedCertificate* cert) const override {
return mock_chrome_root_constraints_;
}
bssl::TrustStore* eutl_trust_store() override { return &eutl_trust_store_; }
void SetMockChromeRootConstraints(
std::vector<StaticChromeRootCertConstraints> chrome_root_constraints) {
mock_chrome_root_constraints_.clear();
for (const auto& constraint : chrome_root_constraints) {
mock_chrome_root_constraints_.emplace_back(constraint);
}
}
void AddMockEutlRoot(CRYPTO_BUFFER* der_cert) {
auto parsed_cert =
bssl::ParsedCertificate::Create(bssl::UpRef(der_cert), {}, nullptr);
ASSERT_TRUE(parsed_cert);
eutl_trust_store_.AddTrustAnchor(std::move(parsed_cert));
}
#endif
private:
bssl::TrustStoreCollection trust_store_;
bool mock_is_known_root_ = false;
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
bool mock_is_locally_trusted_root_ = false;
std::vector<ChromeRootCertConstraints> mock_chrome_root_constraints_;
bssl::TrustStoreInMemory eutl_trust_store_;
#endif
};
class BlockingTrustStore : public bssl::TrustStore {
public:
bssl::CertificateTrust GetTrust(
const bssl::ParsedCertificate* cert) override {
return backing_trust_store_.GetTrust(cert);
}
void SyncGetIssuersOf(const bssl::ParsedCertificate* cert,
bssl::ParsedCertificateList* issuers) override {
sync_get_issuer_started_event_.Signal();
sync_get_issuer_ok_to_finish_event_.Wait();
backing_trust_store_.SyncGetIssuersOf(cert, issuers);
}
base::WaitableEvent sync_get_issuer_started_event_;
base::WaitableEvent sync_get_issuer_ok_to_finish_event_;
bssl::TrustStoreInMemory backing_trust_store_;
};
class MockCTVerifier : public CTVerifier {
public:
MOCK_CONST_METHOD6(Verify,
void(X509Certificate*,
std::string_view,
std::string_view,
base::Time current_time,
SignedCertificateTimestampAndStatusList*,
const NetLogWithSource&));
};
class MockCTPolicyEnforcer : public CTPolicyEnforcer {
public:
MOCK_CONST_METHOD4(CheckCompliance,
ct::CTPolicyCompliance(X509Certificate* cert,
const ct::SCTList&,
base::Time,
const NetLogWithSource&));
MOCK_CONST_METHOD1(GetLogDisqualificationTime,
std::optional<base::Time>(std::string_view log_id));
MOCK_CONST_METHOD0(IsCtEnabled, bool());
protected:
~MockCTPolicyEnforcer() override = default;
};
class MockRequireCTDelegate : public RequireCTDelegate {
public:
MOCK_CONST_METHOD3(
IsCTRequiredForHost,
CTRequirementLevel(std::string_view host,
const X509Certificate* chain,
const std::vector<SHA256HashValue>& hashes));
protected:
~MockRequireCTDelegate() override = default;
};
}
class CertVerifyProcBuiltinTest : public ::testing::Test {
public:
void SetUp() override {
cert_net_fetcher_ = base::MakeRefCounted<CertNetFetcherURLRequest>();
InitializeVerifyProc(CreateParams({}));
context_ = CreateTestURLRequestContextBuilder()->Build();
cert_net_fetcher_->SetURLRequestContext(context_.get());
}
void TearDown() override { cert_net_fetcher_->Shutdown(); }
CertVerifyProc::InstanceParams CreateParams(
const CertificateList& additional_trust_anchors,
const CertificateList&
additional_trust_anchors_with_enforced_constraints = {},
const CertificateList& additional_distrusted_certificates = {}) {
CertVerifyProc::InstanceParams instance_params;
instance_params.additional_trust_anchors =
net::x509_util::ParseAllValidCerts(additional_trust_anchors);
instance_params.additional_trust_anchors_with_enforced_constraints =
net::x509_util::ParseAllValidCerts(
additional_trust_anchors_with_enforced_constraints);
std::vector<std::vector<uint8_t>> distrusted_spkis;
for (const auto& x509_cert : additional_distrusted_certificates) {
std::shared_ptr<const bssl::ParsedCertificate> cert =
bssl::ParsedCertificate::Create(
bssl::UpRef(x509_cert->cert_buffer()),
net::x509_util::DefaultParseCertificateOptions(),
nullptr);
EXPECT_TRUE(cert);
distrusted_spkis.push_back(base::ToVector(cert->tbs().spki_tlv));
}
instance_params.additional_distrusted_spkis = distrusted_spkis;
return instance_params;
}
void InitializeVerifyProc(
const CertVerifyProc::InstanceParams& instance_params,
std::optional<base::Time> current_time = std::nullopt) {
auto mock_system_trust_store = std::make_unique<MockSystemTrustStore>();
mock_system_trust_store_ = mock_system_trust_store.get();
auto mock_ct_verifier = std::make_unique<MockCTVerifier>();
mock_ct_verifier_ = mock_ct_verifier.get();
mock_ct_policy_enforcer_ = base::MakeRefCounted<MockCTPolicyEnforcer>();
std::optional<network_time::TimeTracker> time_tracker;
if (current_time.has_value()) {
time_tracker =
network_time::TimeTracker(base::Time::Now(), base::TimeTicks::Now(),
current_time.value(), base::TimeDelta());
}
verify_proc_ = CreateCertVerifyProcBuiltin(
cert_net_fetcher_, CRLSet::EmptyCRLSetForTesting(),
std::move(mock_ct_verifier), mock_ct_policy_enforcer_,
std::move(mock_system_trust_store), instance_params, time_tracker);
}
void Verify(scoped_refptr<X509Certificate> cert,
const std::string& hostname,
int flags,
CertVerifyResult* verify_result,
NetLogSource* out_source,
CompletionOnceCallback callback) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(
&VerifyOnWorkerThread, verify_proc_, std::move(cert), hostname,
std::string(),
std::string(), flags, verify_result, out_source),
std::move(callback));
}
void Verify(scoped_refptr<X509Certificate> cert,
const std::string& hostname,
const std::string& ocsp_response,
const std::string& sct_list,
int flags,
CertVerifyResult* verify_result,
NetLogSource* out_source,
CompletionOnceCallback callback) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&VerifyOnWorkerThread, verify_proc_, std::move(cert),
hostname, ocsp_response, sct_list, flags, verify_result,
out_source),
std::move(callback));
}
scoped_refptr<X509Certificate> Verify2QwacBinding(
std::string_view binding,
const std::string& hostname,
base::span<const uint8_t> tls_cert,
NetLogSource* out_source) {
NetLogWithSource net_log(NetLogWithSource::Make(
net::NetLog::Get(), net::NetLogSourceType::CERT_VERIFIER_TASK));
*out_source = net_log.source();
return verify_proc_->Verify2QwacBinding(binding, hostname, tls_cert,
net_log);
}
int Verify2Qwac(scoped_refptr<X509Certificate> cert,
const std::string& hostname,
CertVerifyResult* verify_result,
NetLogSource* out_source) {
NetLogWithSource net_log(NetLogWithSource::Make(
net::NetLog::Get(), net::NetLogSourceType::CERT_VERIFIER_TASK));
*out_source = net_log.source();
return verify_proc_->Verify2Qwac(cert.get(), hostname, verify_result,
net_log);
}
base::test::TaskEnvironment& task_environment() { return task_environment_; }
GURL CreateAndServeCrl(EmbeddedTestServer* test_server,
CertBuilder* crl_issuer,
const std::vector<uint64_t>& revoked_serials,
std::optional<bssl::SignatureAlgorithm>
signature_algorithm = std::nullopt) {
std::string crl = BuildCrl(crl_issuer->GetSubject(), crl_issuer->GetKey(),
revoked_serials, signature_algorithm);
std::string crl_path = MakeRandomPath(".crl");
test_server->RegisterRequestHandler(
base::BindRepeating(&test_server::HandlePrefixedRequest, crl_path,
base::BindRepeating(ServeResponse, HTTP_OK,
"application/pkix-crl", crl)));
return test_server->GetURL(crl_path);
}
void AddTrustStore(bssl::TrustStore* store) {
mock_system_trust_store_->AddTrustStore(store);
}
void SetMockIsKnownRoot(bool is_known_root) {
mock_system_trust_store_->SetMockIsKnownRoot(is_known_root);
}
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
void SetMockIsLocallyTrustedRoot(bool is_locally_trusted_root) {
mock_system_trust_store_->SetMockIsLocallyTrustedRoot(
is_locally_trusted_root);
}
void SetMockChromeRootConstraints(
std::vector<StaticChromeRootCertConstraints> chrome_root_constraints) {
mock_system_trust_store_->SetMockChromeRootConstraints(
std::move(chrome_root_constraints));
}
void AddMockEutlRoot(CRYPTO_BUFFER* der_cert) {
mock_system_trust_store_->AddMockEutlRoot(der_cert);
}
#endif
net::URLRequestContext* context() { return context_.get(); }
MockCTVerifier* mock_ct_verifier() { return mock_ct_verifier_; }
MockCTPolicyEnforcer* mock_ct_policy_enforcer() {
return mock_ct_policy_enforcer_.get();
}
private:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME,
base::test::TaskEnvironment::MainThreadType::IO,
};
CertVerifier::Config config_;
std::unique_ptr<net::URLRequestContext> context_;
scoped_refptr<CertVerifyProc> verify_proc_;
raw_ptr<MockCTVerifier> mock_ct_verifier_ = nullptr;
scoped_refptr<MockCTPolicyEnforcer> mock_ct_policy_enforcer_;
raw_ptr<MockSystemTrustStore> mock_system_trust_store_ = nullptr;
scoped_refptr<CertNetFetcherURLRequest> cert_net_fetcher_;
};
TEST_F(CertVerifyProcBuiltinTest, ShouldBypassHSTS) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTP);
ASSERT_TRUE(test_server.InitializeAndListen());
leaf->SetCrlDistributionPointUrl(
CreateAndServeCrl(&test_server, root.get(), {leaf->GetSerialNumber()}));
test_server.StartAcceptingConnections();
{
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
NetLogSource verify_net_log_source;
CertVerifyResult verify_result;
TestCompletionCallback verify_callback;
context()->transport_security_state()->AddHSTS(
test_server.base_url().GetHost(), base::Time::Now() + base::Seconds(30),
true);
ASSERT_TRUE(context()->transport_security_state()->ShouldUpgradeToSSL(
test_server.base_url().GetHost(), true));
Verify(chain.get(), "www.example.com",
CertVerifyProc::VERIFY_REV_CHECKING_ENABLED,
&verify_result, &verify_net_log_source, verify_callback.callback());
int error = verify_callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
}
}
TEST_F(CertVerifyProcBuiltinTest, SimpleSuccess) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
TEST_F(CertVerifyProcBuiltinTest, CallsCtVerifierAndReturnsSctStatus) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
CertVerifyProc::InstanceParams instance_params = CreateParams(
{root->GetX509Certificate()});
InitializeVerifyProc(instance_params);
net::ScopedTestKnownRoot scoped_known_root(root->GetX509Certificate().get());
constexpr char kHostname[] = "www.example.com";
const std::string kOcspResponse = "OCSP response";
const std::string kSctList = "SCT list";
const std::string kLogId = "CT log id";
const ct::SCTVerifyStatus kSctVerifyStatus = ct::SCT_STATUS_LOG_UNKNOWN;
SignedCertificateTimestampAndStatus sct_and_status;
sct_and_status.sct = base::MakeRefCounted<ct::SignedCertificateTimestamp>();
sct_and_status.sct->log_id = kLogId;
sct_and_status.status = kSctVerifyStatus;
SignedCertificateTimestampAndStatusList sct_and_status_list;
sct_and_status_list.push_back(sct_and_status);
EXPECT_CALL(*mock_ct_verifier(), Verify(_, kOcspResponse, kSctList, _, _, _))
.WillRepeatedly(testing::SetArgPointee<4>(sct_and_status_list));
EXPECT_CALL(*mock_ct_policy_enforcer(), IsCtEnabled())
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_ct_policy_enforcer(), CheckCompliance(_, _, _, _))
.WillRepeatedly(
testing::Return(ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS));
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), kHostname, kOcspResponse, kSctList, 0,
&verify_result, &verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
ASSERT_EQ(verify_result.scts.size(), 1u);
EXPECT_EQ(verify_result.scts.front().status, kSctVerifyStatus);
EXPECT_EQ(verify_result.scts.front().sct->log_id, kLogId);
EXPECT_EQ(verify_result.policy_compliance,
ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS);
EXPECT_EQ(verify_result.ct_requirement_status,
ct::CTRequirementsStatus::CT_NOT_REQUIRED);
}
auto mock_require_ct_delegate = base::MakeRefCounted<MockRequireCTDelegate>();
instance_params.require_ct_delegate = mock_require_ct_delegate;
EXPECT_CALL(*mock_require_ct_delegate, IsCTRequiredForHost(kHostname, _, _))
.WillRepeatedly(
testing::Return(RequireCTDelegate::CTRequirementLevel::REQUIRED));
InitializeVerifyProc(instance_params);
EXPECT_CALL(*mock_ct_verifier(), Verify(_, kOcspResponse, kSctList, _, _, _))
.WillRepeatedly(testing::SetArgPointee<4>(sct_and_status_list));
EXPECT_CALL(*mock_ct_policy_enforcer(), IsCtEnabled())
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_ct_policy_enforcer(), CheckCompliance(_, _, _, _))
.WillRepeatedly(
testing::Return(ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS));
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), kHostname, kOcspResponse, kSctList, 0,
&verify_result, &verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERTIFICATE_TRANSPARENCY_REQUIRED));
ASSERT_EQ(verify_result.scts.size(), 1u);
EXPECT_EQ(verify_result.scts.front().status, kSctVerifyStatus);
EXPECT_EQ(verify_result.scts.front().sct->log_id, kLogId);
EXPECT_EQ(verify_result.policy_compliance,
ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS);
EXPECT_EQ(verify_result.ct_requirement_status,
ct::CTRequirementsStatus::CT_REQUIREMENTS_NOT_MET);
}
}
TEST_F(CertVerifyProcBuiltinTest, CtIsRequiredAndCtVerificationComplies) {
constexpr char kHostname[] = "www.example.com";
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
CertVerifyProc::InstanceParams instance_params = CreateParams(
{root->GetX509Certificate()});
auto mock_require_ct_delegate = base::MakeRefCounted<MockRequireCTDelegate>();
instance_params.require_ct_delegate = mock_require_ct_delegate;
EXPECT_CALL(*mock_require_ct_delegate, IsCTRequiredForHost(kHostname, _, _))
.WillRepeatedly(
testing::Return(RequireCTDelegate::CTRequirementLevel::REQUIRED));
InitializeVerifyProc(instance_params);
net::ScopedTestKnownRoot scoped_known_root(root->GetX509Certificate().get());
const std::string kOcspResponse = "OCSP response";
const std::string kSctList = "SCT list";
const std::string kLogId = "CT log id";
const ct::SCTVerifyStatus kSctVerifyStatus = ct::SCT_STATUS_LOG_UNKNOWN;
SignedCertificateTimestampAndStatus sct_and_status;
sct_and_status.sct = base::MakeRefCounted<ct::SignedCertificateTimestamp>();
sct_and_status.sct->log_id = kLogId;
sct_and_status.status = kSctVerifyStatus;
SignedCertificateTimestampAndStatusList sct_and_status_list;
sct_and_status_list.push_back(sct_and_status);
InitializeVerifyProc(instance_params);
EXPECT_CALL(*mock_ct_verifier(), Verify(_, kOcspResponse, kSctList, _, _, _))
.WillRepeatedly(testing::SetArgPointee<4>(sct_and_status_list));
EXPECT_CALL(*mock_ct_policy_enforcer(), IsCtEnabled())
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_ct_policy_enforcer(), CheckCompliance(_, _, _, _))
.WillRepeatedly(
testing::Return(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS));
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509CertificateChain().get(), kHostname, kOcspResponse,
kSctList, 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
ASSERT_EQ(verify_result.scts.size(), 1u);
EXPECT_EQ(verify_result.scts.front().status, kSctVerifyStatus);
EXPECT_EQ(verify_result.scts.front().sct->log_id, kLogId);
EXPECT_EQ(verify_result.policy_compliance,
ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS);
EXPECT_EQ(verify_result.ct_requirement_status,
ct::CTRequirementsStatus::CT_REQUIREMENTS_MET);
}
TEST_F(CertVerifyProcBuiltinTest, DefaultCtComplianceIsNotAvailable) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
const std::string kOcspResponse = "OCSP response";
const std::string kSctList = "SCT list";
const std::string kLogId = "CT log id";
const ct::SCTVerifyStatus kSctVerifyStatus = ct::SCT_STATUS_OK;
SignedCertificateTimestampAndStatus sct_and_status;
sct_and_status.sct = base::MakeRefCounted<ct::SignedCertificateTimestamp>();
sct_and_status.sct->log_id = kLogId;
sct_and_status.status = kSctVerifyStatus;
SignedCertificateTimestampAndStatusList sct_and_status_list;
sct_and_status_list.push_back(sct_and_status);
EXPECT_CALL(*mock_ct_verifier(), Verify(_, kOcspResponse, kSctList, _, _, _))
.WillOnce(testing::SetArgPointee<4>(sct_and_status_list));
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", kOcspResponse, kSctList, 0,
&verify_result, &verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
ASSERT_EQ(verify_result.scts.size(), 1u);
EXPECT_EQ(verify_result.scts.front().status, kSctVerifyStatus);
EXPECT_EQ(verify_result.scts.front().sct->log_id, kLogId);
EXPECT_EQ(verify_result.policy_compliance,
ct::CTPolicyCompliance::CT_POLICY_COMPLIANCE_DETAILS_NOT_AVAILABLE);
}
TEST_F(CertVerifyProcBuiltinTest,
DefaultCtComplianceIsNotAvailableWhenCtDisabled) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
CertVerifyProc::InstanceParams instance_params = CreateParams(
{root->GetX509Certificate()});
InitializeVerifyProc(instance_params);
net::ScopedTestKnownRoot scoped_known_root(root->GetX509Certificate().get());
const std::string kOcspResponse = "OCSP response";
const std::string kSctList = "SCT list";
const std::string kLogId = "CT log id";
const ct::SCTVerifyStatus kSctVerifyStatus = ct::SCT_STATUS_OK;
SignedCertificateTimestampAndStatus sct_and_status;
sct_and_status.sct = base::MakeRefCounted<ct::SignedCertificateTimestamp>();
sct_and_status.sct->log_id = kLogId;
sct_and_status.status = kSctVerifyStatus;
SignedCertificateTimestampAndStatusList sct_and_status_list;
sct_and_status_list.push_back(sct_and_status);
EXPECT_CALL(*mock_ct_verifier(), Verify(_, kOcspResponse, kSctList, _, _, _))
.WillOnce(testing::SetArgPointee<4>(sct_and_status_list));
EXPECT_CALL(*mock_ct_policy_enforcer(), IsCtEnabled())
.WillRepeatedly(testing::Return(false));
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", kOcspResponse, kSctList, 0,
&verify_result, &verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
ASSERT_EQ(verify_result.scts.size(), 1u);
EXPECT_EQ(verify_result.scts.front().status, kSctVerifyStatus);
EXPECT_EQ(verify_result.scts.front().sct->log_id, kLogId);
EXPECT_EQ(verify_result.policy_compliance,
ct::CTPolicyCompliance::CT_POLICY_COMPLIANCE_DETAILS_NOT_AVAILABLE);
}
#if defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
TEST_F(CertVerifyProcBuiltinTest, EVCertStatusMaintainedForCompliantCert) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
static const char kEVTestCertPolicy[] = "1.2.3.4";
leaf->SetCertificatePolicies({kEVTestCertPolicy});
ScopedTestEVPolicy scoped_test_ev_policy(
EVRootCAMetadata::GetInstance(),
X509Certificate::CalculateFingerprint256(root->GetCertBuffer()),
kEVTestCertPolicy);
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
EXPECT_CALL(*mock_ct_verifier(), Verify(_, _, _, _, _, _));
EXPECT_CALL(*mock_ct_policy_enforcer(), IsCtEnabled())
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_ct_policy_enforcer(), CheckCompliance(_, _, _, _))
.WillRepeatedly(
testing::Return(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS));
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
EXPECT_EQ(verify_result.policy_compliance,
ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS);
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_IS_EV);
}
#endif
TEST_F(CertVerifyProcBuiltinTest, DistrustedIntermediate) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()},
{},
{intermediate->GetX509Certificate()}));
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
EXPECT_EQ(1u, verify_result.verified_cert->intermediate_buffers().size());
}
TEST_F(CertVerifyProcBuiltinTest, AddedRootWithConstraints) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
root->SetNameConstraintsDnsNames({"example.org"},
{});
InitializeVerifyProc(CreateParams(
{},
{root->GetX509Certificate()},
{}));
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
}
TEST_F(CertVerifyProcBuiltinTest, AddedRootWithConstraintsNotEnforced) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
root->SetNameConstraintsDnsNames({"example.org"},
{});
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()},
{},
{}));
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
TEST_F(CertVerifyProcBuiltinTest, AddedRootWithOutsideDNSConstraints) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
CertVerifyProc::InstanceParams instance_params;
std::shared_ptr<const bssl::ParsedCertificate> root_cert =
bssl::ParsedCertificate::Create(
bssl::UpRef(root->GetX509Certificate()->cert_buffer()),
net::x509_util::DefaultParseCertificateOptions(), nullptr);
ASSERT_TRUE(root_cert);
CertVerifyProc::CertificateWithConstraints cert_with_constraints;
cert_with_constraints.certificate = std::move(root_cert);
cert_with_constraints.permitted_dns_names.push_back("example.com");
instance_params.additional_trust_anchors_with_constraints.push_back(
cert_with_constraints);
InitializeVerifyProc(instance_params);
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
TEST_F(CertVerifyProcBuiltinTest,
AddedRootWithOutsideDNSConstraintsNotMatched) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
CertVerifyProc::InstanceParams instance_params;
std::shared_ptr<const bssl::ParsedCertificate> root_cert =
bssl::ParsedCertificate::Create(
bssl::UpRef(root->GetX509Certificate()->cert_buffer()),
net::x509_util::DefaultParseCertificateOptions(), nullptr);
ASSERT_TRUE(root_cert);
CertVerifyProc::CertificateWithConstraints cert_with_constraints;
cert_with_constraints.certificate = std::move(root_cert);
cert_with_constraints.permitted_dns_names.push_back("foobar.com");
instance_params.additional_trust_anchors_with_constraints.push_back(
cert_with_constraints);
InitializeVerifyProc(instance_params);
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
}
TEST_F(CertVerifyProcBuiltinTest, AddedRootWithOutsideCIDRConstraints) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
CertVerifyProc::InstanceParams instance_params;
std::shared_ptr<const bssl::ParsedCertificate> root_cert =
bssl::ParsedCertificate::Create(
bssl::UpRef(root->GetX509Certificate()->cert_buffer()),
net::x509_util::DefaultParseCertificateOptions(), nullptr);
ASSERT_TRUE(root_cert);
CertVerifyProc::CertificateWithConstraints cert_with_constraints;
cert_with_constraints.certificate = std::move(root_cert);
cert_with_constraints.permitted_cidrs.push_back(
{net::IPAddress(192, 168, 1, 104), net::IPAddress(255, 255, 255, 0)});
instance_params.additional_trust_anchors_with_constraints.push_back(
cert_with_constraints);
InitializeVerifyProc(instance_params);
leaf->SetSubjectAltNames({"www.example.com"},
{net::IPAddress(192, 168, 1, 254)});
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
TEST_F(CertVerifyProcBuiltinTest,
AddedRootWithOutsideCIDRConstraintsNotMatched) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
CertVerifyProc::InstanceParams instance_params = CreateParams({});
std::shared_ptr<const bssl::ParsedCertificate> root_cert =
bssl::ParsedCertificate::Create(
bssl::UpRef(root->GetX509Certificate()->cert_buffer()),
net::x509_util::DefaultParseCertificateOptions(), nullptr);
ASSERT_TRUE(root_cert);
CertVerifyProc::CertificateWithConstraints cert_with_constraints;
cert_with_constraints.certificate = std::move(root_cert);
cert_with_constraints.permitted_cidrs.push_back(
{net::IPAddress(192, 168, 1, 1), net::IPAddress(255, 255, 255, 0)});
instance_params.additional_trust_anchors_with_constraints.push_back(
cert_with_constraints);
InitializeVerifyProc(instance_params);
leaf->SetSubjectAltNames({"www.example.com"},
{net::IPAddress(10, 2, 2, 2)});
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
}
TEST_F(CertVerifyProcBuiltinTest, AddedRootWithBadTime) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
root->SetValidity(base::Time::Now() - base::Days(10),
base::Time::Now() - base::Days(5));
InitializeVerifyProc(CreateParams(
{},
{root->GetX509Certificate()},
{}));
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_DATE_INVALID));
}
TEST_F(CertVerifyProcBuiltinTest, AddedRootWithBadTimeButNotEnforced) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
root->SetValidity(base::Time::Now() - base::Days(10),
base::Time::Now() - base::Days(5));
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()},
{},
{}));
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
TEST_F(CertVerifyProcBuiltinTest, TimeTracker) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
root->SetValidity(base::Time::Now() - base::Days(10),
base::Time::Now() - base::Days(5));
InitializeVerifyProc(
CreateParams(
{},
{root->GetX509Certificate()},
{}),
base::Time::Now() - base::Days(7));
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
TEST_F(CertVerifyProcBuiltinTest, TimeTrackerFailureIsRetriedWithSystemTime) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
root->SetValidity(base::Time::Now() - base::Days(10),
base::Time::Now() + base::Days(10));
InitializeVerifyProc(
CreateParams(
{},
{root->GetX509Certificate()},
{}),
base::Time::Now() + base::Days(20));
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
TEST_F(CertVerifyProcBuiltinTest,
TimeTrackerRevocationFailureIsRetriedWithSystemTime) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
root->SetValidity(base::Time::Now() - base::Days(3),
base::Time::Now() + base::Days(2));
InitializeVerifyProc(
CreateParams(
{},
{root->GetX509Certificate()},
{}),
base::Time::Now() - base::Days(2));
EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTP);
ASSERT_TRUE(test_server.InitializeAndListen());
leaf->SetCrlDistributionPointUrl(
CreateAndServeCrl(&test_server, root.get(), {1234}));
test_server.StartAcceptingConnections();
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com",
CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS,
&verify_result, &verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
TEST_F(CertVerifyProcBuiltinTest, CRLNotCheckedForKnownRoots) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTP);
ASSERT_TRUE(test_server.InitializeAndListen());
leaf->SetCrlDistributionPointUrl(
CreateAndServeCrl(&test_server, root.get(), {leaf->GetSerialNumber()}));
test_server.StartAcceptingConnections();
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
NetLogSource verify_net_log_source;
{
CertVerifyResult verify_result;
TestCompletionCallback verify_callback;
Verify(chain.get(), "www.example.com",
CertVerifyProc::VERIFY_REV_CHECKING_ENABLED,
&verify_result, &verify_net_log_source, verify_callback.callback());
int error = verify_callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
}
{
SetMockIsKnownRoot(true);
CertVerifyResult verify_result;
TestCompletionCallback verify_callback;
Verify(chain.get(), "www.example.com",
CertVerifyProc::VERIFY_REV_CHECKING_ENABLED,
&verify_result, &verify_net_log_source, verify_callback.callback());
int error = verify_callback.WaitForResult();
EXPECT_THAT(error, IsOk());
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
}
}
TEST_F(CertVerifyProcBuiltinTest, RevocationCheckDeadlineCRL) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
const base::TimeDelta timeout_increment =
CertNetFetcherURLRequest::GetDefaultTimeoutForTesting() +
base::Milliseconds(1);
const int expected_request_count =
base::ClampFloor(GetCertVerifyProcBuiltinTimeLimitForTesting() /
timeout_increment) +
1;
EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTP);
ASSERT_TRUE(test_server.InitializeAndListen());
std::vector<GURL> crl_urls;
std::vector<base::RunLoop> runloops(expected_request_count);
for (int i = 0; i < expected_request_count; ++i) {
std::string path = base::StringPrintf("/hung/%i", i);
crl_urls.emplace_back(test_server.GetURL(path));
test_server.RegisterRequestHandler(
base::BindRepeating(&test_server::HandlePrefixedRequest, path,
base::BindRepeating(&HangRequestAndCallback,
runloops[i].QuitClosure())));
}
for (int i = expected_request_count; i < expected_request_count + 1; ++i) {
std::string path = base::StringPrintf("/failtest/%i", i);
crl_urls.emplace_back(test_server.GetURL(path));
test_server.RegisterRequestHandler(base::BindRepeating(
&test_server::HandlePrefixedRequest, path,
base::BindRepeating(FailRequestAndFailTest,
"additional request made after deadline exceeded",
base::SequencedTaskRunner::GetCurrentDefault())));
}
leaf->SetCrlDistributionPointUrls(crl_urls);
test_server.StartAcceptingConnections();
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback verify_callback;
Verify(chain.get(), "www.example.com",
CertVerifyProc::VERIFY_REV_CHECKING_ENABLED,
&verify_result, &verify_net_log_source, verify_callback.callback());
for (int i = 0; i < expected_request_count; i++) {
runloops[i].Run();
task_environment().AdvanceClock(timeout_increment);
}
int error = verify_callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
TEST_F(CertVerifyProcBuiltinTest, RevocationCheckDeadlineOCSP) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
const base::TimeDelta timeout_increment =
CertNetFetcherURLRequest::GetDefaultTimeoutForTesting() +
base::Milliseconds(1);
const int expected_request_count =
base::ClampFloor(GetCertVerifyProcBuiltinTimeLimitForTesting() /
timeout_increment) +
1;
EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTP);
ASSERT_TRUE(test_server.InitializeAndListen());
std::vector<GURL> ocsp_urls;
std::vector<base::RunLoop> runloops(expected_request_count);
for (int i = 0; i < expected_request_count; ++i) {
std::string path = base::StringPrintf("/hung/%i", i);
ocsp_urls.emplace_back(test_server.GetURL(path));
test_server.RegisterRequestHandler(
base::BindRepeating(&test_server::HandlePrefixedRequest, path,
base::BindRepeating(&HangRequestAndCallback,
runloops[i].QuitClosure())));
}
for (int i = expected_request_count; i < expected_request_count + 1; ++i) {
std::string path = base::StringPrintf("/failtest/%i", i);
ocsp_urls.emplace_back(test_server.GetURL(path));
test_server.RegisterRequestHandler(base::BindRepeating(
&test_server::HandlePrefixedRequest, path,
base::BindRepeating(FailRequestAndFailTest,
"additional request made after deadline exceeded",
base::SequencedTaskRunner::GetCurrentDefault())));
}
leaf->SetCaIssuersAndOCSPUrls({}, ocsp_urls);
test_server.StartAcceptingConnections();
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback verify_callback;
Verify(chain.get(), "www.example.com",
CertVerifyProc::VERIFY_REV_CHECKING_ENABLED,
&verify_result, &verify_net_log_source, verify_callback.callback());
for (int i = 0; i < expected_request_count; i++) {
runloops[i].Run();
task_environment().AdvanceClock(timeout_increment);
}
int error = verify_callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
#if defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
TEST_F(CertVerifyProcBuiltinTest, EVNoOCSPRevocationChecks) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
static const char kEVTestCertPolicy[] = "1.2.3.4";
leaf->SetCertificatePolicies({kEVTestCertPolicy});
intermediate->SetCertificatePolicies({kEVTestCertPolicy});
EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTP);
ASSERT_TRUE(test_server.InitializeAndListen());
std::vector<GURL> ocsp_urls;
std::string path = "/failtest";
ocsp_urls.emplace_back(test_server.GetURL(path));
test_server.RegisterRequestHandler(base::BindRepeating(
&test_server::HandlePrefixedRequest, path,
base::BindRepeating(FailRequestAndFailTest,
"no OCSP requests should be sent",
base::SequencedTaskRunner::GetCurrentDefault())));
intermediate->SetCaIssuersAndOCSPUrls({}, ocsp_urls);
test_server.StartAcceptingConnections();
ScopedTestEVPolicy scoped_test_ev_policy(
EVRootCAMetadata::GetInstance(),
X509Certificate::CalculateFingerprint256(root->GetCertBuffer()),
kEVTestCertPolicy);
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
RecordingNetLogObserver net_log_observer(NetLogCaptureMode::kDefault);
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback verify_callback;
Verify(chain.get(), "www.example.com",
0,
&verify_result, &verify_net_log_source, verify_callback.callback());
int error = verify_callback.WaitForResult();
EXPECT_THAT(error, IsOk());
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_IS_EV);
EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
auto events = net_log_observer.GetEntriesForSource(verify_net_log_source);
auto event = std::ranges::find(
events, NetLogEventType::CERT_VERIFY_PROC_PATH_BUILD_ATTEMPT,
&NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::BEGIN, event->phase);
EXPECT_EQ(true, event->params.FindBool("is_ev_attempt"));
event = std::ranges::find(++event, events.end(),
NetLogEventType::CERT_VERIFY_PROC_PATH_BUILT,
&NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::BEGIN, event->phase);
event = std::ranges::find(++event, events.end(),
NetLogEventType::CERT_VERIFY_PROC_PATH_BUILT,
&NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::END, event->phase);
EXPECT_FALSE(event->params.FindString("errors"));
event = std::ranges::find(
++event, events.end(),
NetLogEventType::CERT_VERIFY_PROC_PATH_BUILD_ATTEMPT, &NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::END, event->phase);
EXPECT_EQ(true, event->params.FindBool("has_valid_path"));
}
#endif
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
scoped_refptr<ct::SignedCertificateTimestamp> MakeSct(base::Time t,
std::string_view log_id) {
auto sct = base::MakeRefCounted<ct::SignedCertificateTimestamp>();
sct->timestamp = t;
sct->log_id = log_id;
return sct;
}
TEST_F(CertVerifyProcBuiltinTest,
ChromeRootStoreConstraintSctConstraintsWithCtDisabled) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
ScopedTestRoot scoped_root(root->GetX509Certificate());
EXPECT_CALL(*mock_ct_policy_enforcer(), IsCtEnabled())
.WillRepeatedly(testing::Return(false));
EXPECT_CALL(*mock_ct_verifier(), Verify(_, _, _, _, _, _)).Times(2);
scoped_refptr<X509Certificate> chain = leaf->GetX509Certificate();
ASSERT_TRUE(chain.get());
SetMockChromeRootConstraints(
{{.sct_not_after = base::Time::Now() - base::Days(365)}});
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", std::string(),
std::string(), 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
ASSERT_EQ(verify_result.scts.size(), 0u);
}
SetMockChromeRootConstraints(
{{.sct_all_after = base::Time::Now() + base::Days(365)}});
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", std::string(),
std::string(), 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
ASSERT_EQ(verify_result.scts.size(), 0u);
}
}
TEST_F(CertVerifyProcBuiltinTest, ChromeRootStoreConstraintSctNotAfter) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
ScopedTestRoot scoped_root(root->GetX509Certificate());
const std::string kSctList = "SCT list";
const std::string kLog1 = "log1";
const std::string kLog2 = "log2";
base::Time now = base::Time::Now();
base::Time t1 = now - base::Days(2);
base::Time t2 = now - base::Days(1);
SignedCertificateTimestampAndStatusList sct_and_status_list;
sct_and_status_list.emplace_back(MakeSct(t1, kLog1), ct::SCT_STATUS_OK);
sct_and_status_list.emplace_back(MakeSct(t2, kLog2), ct::SCT_STATUS_OK);
EXPECT_CALL(*mock_ct_verifier(), Verify(_, _, kSctList, _, _, _))
.WillRepeatedly(testing::SetArgPointee<4>(sct_and_status_list));
SetMockChromeRootConstraints({{.sct_not_after = t1}});
EXPECT_CALL(*mock_ct_policy_enforcer(), IsCtEnabled())
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_ct_policy_enforcer(), GetLogDisqualificationTime(kLog1))
.WillRepeatedly(testing::Return(std::nullopt));
EXPECT_CALL(*mock_ct_policy_enforcer(), GetLogDisqualificationTime(kLog2))
.WillRepeatedly(testing::Return(std::nullopt));
EXPECT_CALL(*mock_ct_policy_enforcer(), CheckCompliance(_, _, _, _))
.WillRepeatedly(
testing::Return(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS));
scoped_refptr<X509Certificate> chain = leaf->GetX509Certificate();
ASSERT_TRUE(chain.get());
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", std::string(),
kSctList, 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
ASSERT_EQ(verify_result.scts.size(), 2u);
}
SetMockChromeRootConstraints({{.sct_not_after = t1 - base::Seconds(1)}});
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", std::string(),
kSctList, 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
ASSERT_EQ(verify_result.scts.size(), 2u);
}
}
TEST_F(CertVerifyProcBuiltinTest,
ChromeRootStoreConstraintSctNotAfterLogUnknown) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
ScopedTestRoot scoped_root(root->GetX509Certificate());
const std::string kSctList = "SCT list";
const std::string kLog1 = "log1";
const std::string kLog2 = "log2";
base::Time now = base::Time::Now();
base::Time t1 = now - base::Days(2);
base::Time t2 = now - base::Days(1);
SignedCertificateTimestampAndStatusList sct_and_status_list;
sct_and_status_list.emplace_back(MakeSct(t1, kLog1),
ct::SCT_STATUS_LOG_UNKNOWN);
sct_and_status_list.emplace_back(MakeSct(t2, kLog2), ct::SCT_STATUS_OK);
EXPECT_CALL(*mock_ct_policy_enforcer(), IsCtEnabled())
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_ct_verifier(), Verify(_, _, kSctList, _, _, _))
.WillOnce(testing::SetArgPointee<4>(sct_and_status_list));
SetMockChromeRootConstraints({{.sct_not_after = t1}});
scoped_refptr<X509Certificate> chain = leaf->GetX509Certificate();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", std::string(),
kSctList, 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
ASSERT_EQ(verify_result.scts.size(), 2u);
}
TEST_F(
CertVerifyProcBuiltinTest,
ChromeRootStoreConstraintSctNotAfterFromDisqualifiedLogBeforeDisqualification) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
ScopedTestRoot scoped_root(root->GetX509Certificate());
const std::string kSctList = "SCT list";
const std::string kLog1 = "log1";
const std::string kLog2 = "log2";
base::Time now = base::Time::Now();
base::Time t1 = now - base::Days(2);
base::Time t2 = now - base::Days(1);
SignedCertificateTimestampAndStatusList sct_and_status_list;
sct_and_status_list.emplace_back(MakeSct(t1, kLog1), ct::SCT_STATUS_OK);
sct_and_status_list.emplace_back(MakeSct(t2, kLog2), ct::SCT_STATUS_OK);
EXPECT_CALL(*mock_ct_verifier(), Verify(_, _, kSctList, _, _, _))
.WillOnce(testing::SetArgPointee<4>(sct_and_status_list));
SetMockChromeRootConstraints({{.sct_not_after = t1}});
EXPECT_CALL(*mock_ct_policy_enforcer(), IsCtEnabled())
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_ct_policy_enforcer(), GetLogDisqualificationTime(kLog1))
.WillRepeatedly(testing::Return(t2));
EXPECT_CALL(*mock_ct_policy_enforcer(), GetLogDisqualificationTime(kLog2))
.WillRepeatedly(testing::Return(std::nullopt));
EXPECT_CALL(*mock_ct_policy_enforcer(), CheckCompliance(_, _, _, _))
.WillRepeatedly(
testing::Return(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS));
scoped_refptr<X509Certificate> chain = leaf->GetX509Certificate();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", std::string(),
kSctList, 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
TEST_F(
CertVerifyProcBuiltinTest,
ChromeRootStoreConstraintSctNotAfterFromDisqualifiedLogAfterDisqualification) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
ScopedTestRoot scoped_root(root->GetX509Certificate());
const std::string kSctList = "SCT list";
const std::string kLog1 = "log1";
const std::string kLog2 = "log2";
base::Time now = base::Time::Now();
base::Time t1 = now - base::Days(2);
base::Time t2 = now - base::Days(1);
SignedCertificateTimestampAndStatusList sct_and_status_list;
sct_and_status_list.emplace_back(MakeSct(t1, kLog1), ct::SCT_STATUS_OK);
sct_and_status_list.emplace_back(MakeSct(t2, kLog2), ct::SCT_STATUS_OK);
EXPECT_CALL(*mock_ct_verifier(), Verify(_, _, kSctList, _, _, _))
.WillOnce(testing::SetArgPointee<4>(sct_and_status_list));
SetMockChromeRootConstraints({{.sct_not_after = t1}});
EXPECT_CALL(*mock_ct_policy_enforcer(), IsCtEnabled())
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_ct_policy_enforcer(), GetLogDisqualificationTime(kLog1))
.WillRepeatedly(testing::Return(t1));
EXPECT_CALL(*mock_ct_policy_enforcer(), GetLogDisqualificationTime(kLog2))
.WillRepeatedly(testing::Return(std::nullopt));
EXPECT_CALL(*mock_ct_policy_enforcer(), CheckCompliance(_, _, _, _))
.WillRepeatedly(
testing::Return(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS));
scoped_refptr<X509Certificate> chain = leaf->GetX509Certificate();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", std::string(),
kSctList, 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
TEST_F(CertVerifyProcBuiltinTest,
ChromeRootStoreConstraintSctNotAfterFromFutureDisqualifiedLog) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
ScopedTestRoot scoped_root(root->GetX509Certificate());
const std::string kSctList = "SCT list";
const std::string kLog1 = "log1";
const std::string kLog2 = "log2";
base::Time now = base::Time::Now();
base::Time t1 = now - base::Days(2);
base::Time future_t = now + base::Days(1);
SignedCertificateTimestampAndStatusList sct_and_status_list;
sct_and_status_list.emplace_back(MakeSct(t1, kLog1), ct::SCT_STATUS_OK);
EXPECT_CALL(*mock_ct_verifier(), Verify(_, _, kSctList, _, _, _))
.WillOnce(testing::SetArgPointee<4>(sct_and_status_list));
SetMockChromeRootConstraints({{.sct_not_after = t1}});
EXPECT_CALL(*mock_ct_policy_enforcer(), IsCtEnabled())
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_ct_policy_enforcer(), GetLogDisqualificationTime(kLog1))
.WillRepeatedly(testing::Return(future_t));
EXPECT_CALL(*mock_ct_policy_enforcer(), CheckCompliance(_, _, _, _))
.WillRepeatedly(
testing::Return(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS));
scoped_refptr<X509Certificate> chain = leaf->GetX509Certificate();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", std::string(),
kSctList, 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
TEST_F(CertVerifyProcBuiltinTest, ChromeRootStoreConstraintSctAllAfter) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
ScopedTestRoot scoped_root(root->GetX509Certificate());
const std::string kSctList = "SCT list";
const std::string kLog1 = "log1";
const std::string kLog2 = "log2";
base::Time now = base::Time::Now();
base::Time t0 = now - base::Days(3);
base::Time t1 = now - base::Days(2);
base::Time t2 = now - base::Days(1);
SignedCertificateTimestampAndStatusList sct_and_status_list;
sct_and_status_list.emplace_back(MakeSct(t1, kLog1), ct::SCT_STATUS_OK);
sct_and_status_list.emplace_back(MakeSct(t2, kLog2), ct::SCT_STATUS_OK);
EXPECT_CALL(*mock_ct_verifier(), Verify(_, _, kSctList, _, _, _))
.WillRepeatedly(testing::SetArgPointee<4>(sct_and_status_list));
SetMockChromeRootConstraints({{.sct_all_after = t0}});
EXPECT_CALL(*mock_ct_policy_enforcer(), IsCtEnabled())
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_ct_policy_enforcer(), GetLogDisqualificationTime(kLog1))
.WillRepeatedly(testing::Return(std::nullopt));
EXPECT_CALL(*mock_ct_policy_enforcer(), GetLogDisqualificationTime(kLog2))
.WillRepeatedly(testing::Return(std::nullopt));
EXPECT_CALL(*mock_ct_policy_enforcer(), CheckCompliance(_, _, _, _))
.WillRepeatedly(
testing::Return(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS));
scoped_refptr<X509Certificate> chain = leaf->GetX509Certificate();
ASSERT_TRUE(chain.get());
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", std::string(),
kSctList, 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
ASSERT_EQ(verify_result.scts.size(), 2u);
}
SetMockChromeRootConstraints({{.sct_all_after = t1}});
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", std::string(),
kSctList, 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
ASSERT_EQ(verify_result.scts.size(), 2u);
}
}
std::string CurVersionString() {
return version_info::GetVersion().GetString();
}
std::string NextVersionString() {
const std::vector<uint32_t>& components =
version_info::GetVersion().components();
return base::Version(
{components[0], components[1], components[2], components[3] + 1})
.GetString();
}
std::string PrevVersionString() {
const std::vector<uint32_t>& components =
version_info::GetVersion().components();
if (components[3] > 0) {
return base::Version(
{components[0], components[1], components[2], components[3] - 1})
.GetString();
} else {
return base::Version(
{components[0], components[1], components[2] - 1, UINT32_MAX})
.GetString();
}
}
TEST_F(CertVerifyProcBuiltinTest, ChromeRootStoreConstraintMinVersion) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
ScopedTestRoot scoped_root(root->GetX509Certificate());
scoped_refptr<X509Certificate> chain = leaf->GetX509Certificate();
ASSERT_TRUE(chain.get());
SetMockChromeRootConstraints({{.min_version = NextVersionString()}});
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
SetMockChromeRootConstraints({{.min_version = CurVersionString()}});
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
}
TEST_F(CertVerifyProcBuiltinTest, ChromeRootStoreConstraintMaxVersion) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
ScopedTestRoot scoped_root(root->GetX509Certificate());
scoped_refptr<X509Certificate> chain = leaf->GetX509Certificate();
ASSERT_TRUE(chain.get());
SetMockChromeRootConstraints({{.max_version_exclusive = CurVersionString()}});
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
SetMockChromeRootConstraints(
{{.max_version_exclusive = NextVersionString()}});
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
}
TEST_F(CertVerifyProcBuiltinTest, ChromeRootStoreConstraintMinAndMaxVersion) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
ScopedTestRoot scoped_root(root->GetX509Certificate());
scoped_refptr<X509Certificate> chain = leaf->GetX509Certificate();
ASSERT_TRUE(chain.get());
SetMockChromeRootConstraints({{.min_version = PrevVersionString(),
.max_version_exclusive = CurVersionString()}});
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
SetMockChromeRootConstraints(
{{.min_version = NextVersionString(),
.max_version_exclusive = NextVersionString()}});
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
SetMockChromeRootConstraints(
{{.min_version = CurVersionString(),
.max_version_exclusive = NextVersionString()}});
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
}
TEST_F(CertVerifyProcBuiltinTest, ChromeRootStoreConstraintNameConstraints) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
ScopedTestRoot scoped_root(root->GetX509Certificate());
{
std::array<std::string_view, 2> permitted_dns_names = {
std::string_view("example.org"),
std::string_view("foo.example.com"),
};
SetMockChromeRootConstraints(
{{.permitted_dns_names = permitted_dns_names}});
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509Certificate(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
{
std::array<std::string_view, 2> permitted_dns_names = {
std::string_view("example.org"),
std::string_view("example.com"),
};
SetMockChromeRootConstraints(
{{.permitted_dns_names = permitted_dns_names}});
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509Certificate(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
}
TEST_F(CertVerifyProcBuiltinTest,
ChromeRootStoreConstraintMultipleConstraints) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
ScopedTestRoot scoped_root(root->GetX509Certificate());
const std::string kSctList = "SCT list";
const std::string kLog1 = "log1";
base::Time now = base::Time::Now();
base::Time t1 = now - base::Days(2);
base::Time t2 = now - base::Days(1);
SignedCertificateTimestampAndStatusList sct_and_status_list;
sct_and_status_list.emplace_back(MakeSct(t2, kLog1), ct::SCT_STATUS_OK);
EXPECT_CALL(*mock_ct_policy_enforcer(), IsCtEnabled())
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_ct_verifier(), Verify(_, _, kSctList, _, _, _))
.WillOnce(testing::SetArgPointee<4>(sct_and_status_list));
EXPECT_CALL(*mock_ct_policy_enforcer(), GetLogDisqualificationTime(kLog1))
.WillRepeatedly(testing::Return(std::nullopt));
EXPECT_CALL(*mock_ct_policy_enforcer(), CheckCompliance(_, _, _, _))
.WillRepeatedly(
testing::Return(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS));
SetMockChromeRootConstraints({{.sct_not_after = t1}, {.sct_all_after = t1}});
scoped_refptr<X509Certificate> chain = leaf->GetX509Certificate();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", std::string(),
kSctList, 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
TEST_F(CertVerifyProcBuiltinTest,
ChromeRootStoreConstraintNotEnforcedIfAnchorLocallyTrusted) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
ScopedTestRoot scoped_root(root->GetX509Certificate());
scoped_refptr<X509Certificate> chain = leaf->GetX509Certificate();
ASSERT_TRUE(chain.get());
SetMockChromeRootConstraints({{.min_version = NextVersionString()}});
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
SetMockIsLocallyTrustedRoot(true);
{
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
}
TEST_F(CertVerifyProcBuiltinTest,
ChromeRootStoreConstraintNotEnforcedIfAnchorAdditionallyTrusted) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
scoped_refptr<X509Certificate> chain = leaf->GetX509Certificate();
ASSERT_TRUE(chain.get());
SetMockChromeRootConstraints({{.min_version = NextVersionString()}});
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
class CertVerifyProcBuiltin1QwacTest
: public CertVerifyProcBuiltinTest,
public testing::WithParamInterface<bool> {
public:
CertVerifyProcBuiltin1QwacTest() {
if (GetParam()) {
feature_list_.InitAndEnableFeature(features::kVerifyQWACs);
} else {
feature_list_.InitAndDisableFeature(features::kVerifyQWACs);
}
}
void ExpectHistogramSample(const base::HistogramTester& histograms,
Verify1QwacResult result) {
if (GetParam()) {
histograms.ExpectUniqueSample("Net.CertVerifier.Qwac.1Qwac", result, 1u);
} else {
histograms.ExpectTotalCount("Net.CertVerifier.Qwac.1Qwac", 0u);
}
}
private:
base::test::ScopedFeatureList feature_list_;
};
TEST_P(CertVerifyProcBuiltin1QwacTest, NotQwac) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
InitializeVerifyProc(CreateParams(
{}));
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509CertificateChain(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_IS_QWAC);
histograms.ExpectTotalCount("Net.CertVerifier.Qwac.1Qwac", 0u);
}
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509CertificateChain(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_IS_QWAC);
ExpectHistogramSample(histograms, Verify1QwacResult::kNotQwac);
}
}
TEST_P(CertVerifyProcBuiltin1QwacTest,
CanUseEutlCertsAsHintsInNormalPathbuilding) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
leaf->SetCertificatePolicies({"2.23.140.1.2.2", "0.4.0.194112.1.5"});
leaf->SetQwacQcStatements({bssl::der::Input(kEtsiQctWebOid)});
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509Certificate(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_IS_QWAC);
histograms.ExpectTotalCount("Net.CertVerifier.Qwac.1Qwac", 0u);
}
AddMockEutlRoot(intermediate->GetCertBuffer());
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509Certificate(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
if (GetParam()) {
EXPECT_THAT(error, IsOk());
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_IS_QWAC);
ASSERT_EQ(2u, verify_result.verified_cert->intermediate_buffers().size());
EXPECT_EQ(intermediate->GetCertBuffer(),
verify_result.verified_cert->intermediate_buffers()[0].get());
EXPECT_EQ(root->GetCertBuffer(),
verify_result.verified_cert->intermediate_buffers()[1].get());
} else {
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_IS_QWAC);
}
ExpectHistogramSample(histograms, Verify1QwacResult::kValid1Qwac);
}
InitializeVerifyProc(CreateParams(
{}));
AddMockEutlRoot(intermediate->GetCertBuffer());
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509Certificate(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_IS_QWAC);
if (GetParam()) {
ASSERT_EQ(1u, verify_result.verified_cert->intermediate_buffers().size());
EXPECT_EQ(intermediate->GetCertBuffer(),
verify_result.verified_cert->intermediate_buffers()[0].get());
} else {
ASSERT_EQ(0u, verify_result.verified_cert->intermediate_buffers().size());
}
histograms.ExpectTotalCount("Net.CertVerifier.Qwac.1Qwac", 0u);
}
}
TEST_P(CertVerifyProcBuiltin1QwacTest, OneQwacRequiresEutl) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
leaf->SetCertificatePolicies({"2.23.140.1.2.2", "0.4.0.194112.1.5"});
leaf->SetQwacQcStatements({bssl::der::Input(kEtsiQctWebOid)});
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509CertificateChain(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_IS_QWAC);
ExpectHistogramSample(histograms, Verify1QwacResult::kFailedVerification);
}
AddMockEutlRoot(intermediate->GetCertBuffer());
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509CertificateChain(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
EXPECT_EQ(GetParam(), !!(verify_result.cert_status & CERT_STATUS_IS_QWAC));
ExpectHistogramSample(histograms, Verify1QwacResult::kValid1Qwac);
}
}
TEST_P(CertVerifyProcBuiltin1QwacTest, OneQwacRequiresPolicies) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
leaf->SetCertificatePolicies({"2.23.140.1.2.2"});
leaf->SetQwacQcStatements({bssl::der::Input(kEtsiQctWebOid)});
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
AddMockEutlRoot(intermediate->GetCertBuffer());
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509CertificateChain(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_IS_QWAC);
ExpectHistogramSample(histograms, Verify1QwacResult::kInconsistentBits);
}
leaf->SetCertificatePolicies({"2.23.140.1.2.2", "0.4.0.194112.1.5"});
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509CertificateChain(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
EXPECT_EQ(GetParam(), !!(verify_result.cert_status & CERT_STATUS_IS_QWAC));
ExpectHistogramSample(histograms, Verify1QwacResult::kValid1Qwac);
}
}
TEST_P(CertVerifyProcBuiltin1QwacTest, OneQwacRequiresQcStatements) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
leaf->SetCertificatePolicies({"2.23.140.1.2.2", "0.4.0.194112.1.5"});
constexpr uint8_t kEtsiQctEsealOid[] = {0x04, 0x00, 0x8e, 0x46,
0x01, 0x06, 0x02};
leaf->SetQwacQcStatements({bssl::der::Input(kEtsiQctEsealOid)});
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
AddMockEutlRoot(intermediate->GetCertBuffer());
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509CertificateChain(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_IS_QWAC);
ExpectHistogramSample(histograms, Verify1QwacResult::kInconsistentBits);
}
leaf->SetQwacQcStatements({bssl::der::Input(kEtsiQctWebOid)});
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509CertificateChain(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
EXPECT_EQ(GetParam(), !!(verify_result.cert_status & CERT_STATUS_IS_QWAC));
ExpectHistogramSample(histograms, Verify1QwacResult::kValid1Qwac);
}
}
TEST_P(CertVerifyProcBuiltin1QwacTest, OneQwacCanBuildAlternatePath) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
leaf->SetCertificatePolicies({"2.23.140.1.2.2", "0.4.0.194112.1.5"});
leaf->SetQwacQcStatements({bssl::der::Input(kEtsiQctWebOid)});
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
auto [unused, root2] = CertBuilder::CreateSimpleChain2();
CertBuilder eutl_intermediate(intermediate->GetCertBuffer(),
root2.get());
eutl_intermediate.SetSubjectTLV(
base::as_byte_span(intermediate->GetSubject()));
eutl_intermediate.SetKey(bssl::UpRef(intermediate->GetKey()));
eutl_intermediate.SetSubjectKeyIdentifier(
intermediate->GetSubjectKeyIdentifier());
AddMockEutlRoot(eutl_intermediate.GetCertBuffer());
RecordingNetLogObserver net_log_observer(NetLogCaptureMode::kDefault);
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(leaf->GetX509CertificateChain(), "www.example.com",
0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
EXPECT_EQ(GetParam(), !!(verify_result.cert_status & CERT_STATUS_IS_QWAC));
ASSERT_EQ(2u, verify_result.verified_cert->intermediate_buffers().size());
EXPECT_EQ(intermediate->GetCertBuffer(),
verify_result.verified_cert->intermediate_buffers()[0].get());
EXPECT_EQ(root->GetCertBuffer(),
verify_result.verified_cert->intermediate_buffers()[1].get());
auto events = net_log_observer.GetEntriesForSource(verify_net_log_source);
auto event = std::ranges::find(
events, NetLogEventType::CERT_VERIFY_PROC_PATH_BUILD_ATTEMPT,
&NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::BEGIN, event->phase);
EXPECT_EQ(std::nullopt, event->params.FindBool("is_qwac_attempt"));
event = std::ranges::find(++event, events.end(),
NetLogEventType::CERT_VERIFY_PROC_PATH_BUILT,
&NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::BEGIN, event->phase);
event = std::ranges::find(++event, events.end(),
NetLogEventType::CERT_VERIFY_PROC_PATH_BUILT,
&NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::END, event->phase);
EXPECT_EQ(true, event->params.FindBool("is_valid"));
base::Value::List* pem_certs = event->params.FindList("certificates");
ASSERT_TRUE(pem_certs);
EXPECT_THAT(ParseNetLogCertificatesList(*pem_certs),
testing::ElementsAre(leaf->GetDER(), intermediate->GetDER(),
root->GetDER()));
event = std::ranges::find(
++event, events.end(),
NetLogEventType::CERT_VERIFY_PROC_PATH_BUILD_ATTEMPT, &NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::END, event->phase);
EXPECT_EQ(true, event->params.FindBool("has_valid_path"));
event = std::ranges::find(
++event, events.end(),
NetLogEventType::CERT_VERIFY_PROC_PATH_BUILD_ATTEMPT, &NetLogEntry::type);
if (!GetParam()) {
ASSERT_EQ(event, events.end());
return;
}
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::BEGIN, event->phase);
EXPECT_EQ(true, event->params.FindBool("is_qwac_attempt"));
event = std::ranges::find(++event, events.end(),
NetLogEventType::CERT_VERIFY_PROC_PATH_BUILT,
&NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::BEGIN, event->phase);
event = std::ranges::find(++event, events.end(),
NetLogEventType::CERT_VERIFY_PROC_PATH_BUILT,
&NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::END, event->phase);
EXPECT_EQ(true, event->params.FindBool("is_valid"));
pem_certs = event->params.FindList("certificates");
ASSERT_TRUE(pem_certs);
EXPECT_THAT(ParseNetLogCertificatesList(*pem_certs),
testing::ElementsAre(leaf->GetDER(), eutl_intermediate.GetDER()));
event = std::ranges::find(
++event, events.end(),
NetLogEventType::CERT_VERIFY_PROC_PATH_BUILD_ATTEMPT, &NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::END, event->phase);
EXPECT_EQ(true, event->params.FindBool("has_valid_path"));
event = std::ranges::find(
++event, events.end(),
NetLogEventType::CERT_VERIFY_PROC_PATH_BUILD_ATTEMPT, &NetLogEntry::type);
ASSERT_EQ(event, events.end());
}
INSTANTIATE_TEST_SUITE_P(, CertVerifyProcBuiltin1QwacTest, testing::Bool());
class CertVerifyProcBuiltin2QwacTest : public CertVerifyProcBuiltinTest {
public:
void ExpectHistogramSample(const base::HistogramTester& histograms,
Verify2QwacBindingResult result) {
histograms.ExpectUniqueSample("Net.CertVerifier.Qwac.2QwacBinding", result,
1u);
}
void ExpectNoHistogramSample(const base::HistogramTester& histograms) {
histograms.ExpectTotalCount("Net.CertVerifier.Qwac.2QwacBinding", 0);
}
};
TEST_F(CertVerifyProcBuiltin2QwacTest, InvalidCertificate) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
intermediate->SetCertificatePolicies({"2.5.29.32.0"});
leaf->SetCertificatePolicies({"0.4.0.194112.1.6"});
leaf->SetQwacQcStatements({bssl::der::Input(kEtsiQctWebOid)});
leaf->SetExtendedKeyUsages({bssl::der::Input(kIdKpTlsBinding)});
leaf->SetExtension(bssl::der::Input(bssl::kBasicConstraintsOid),
"invalid extension value", true);
InitializeVerifyProc(CreateParams({}));
AddMockEutlRoot(root->GetCertBuffer());
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
int error = Verify2Qwac(leaf->GetX509CertificateChain(), "www.example.com",
&verify_result, &verify_net_log_source);
EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
ExpectHistogramSample(histograms,
Verify2QwacBindingResult::kCertLeafParsingError);
}
}
TEST_F(CertVerifyProcBuiltin2QwacTest, TwoQwacRequiresEutl) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
intermediate->SetCertificatePolicies({"2.5.29.32.0"});
leaf->SetCertificatePolicies({"0.4.0.194112.1.6"});
leaf->SetQwacQcStatements({bssl::der::Input(kEtsiQctWebOid)});
leaf->SetExtendedKeyUsages({bssl::der::Input(kIdKpTlsBinding)});
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
int error = Verify2Qwac(leaf->GetX509CertificateChain(), "www.example.com",
&verify_result, &verify_net_log_source);
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_AUTHORITY_INVALID);
ExpectHistogramSample(histograms,
Verify2QwacBindingResult::kCertAuthorityInvalid);
EXPECT_EQ(leaf->GetCertBuffer(),
verify_result.verified_cert->cert_buffer());
ASSERT_EQ(1u, verify_result.verified_cert->intermediate_buffers().size());
EXPECT_EQ(intermediate->GetCertBuffer(),
verify_result.verified_cert->intermediate_buffers()[0].get());
}
InitializeVerifyProc(CreateParams({}));
AddMockEutlRoot(root->GetCertBuffer());
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
int error = Verify2Qwac(leaf->GetX509CertificateChain(), "www.example.com",
&verify_result, &verify_net_log_source);
EXPECT_THAT(error, IsOk());
EXPECT_FALSE(IsCertStatusError(verify_result.cert_status));
ExpectNoHistogramSample(histograms);
EXPECT_EQ(leaf->GetCertBuffer(),
verify_result.verified_cert->cert_buffer());
ASSERT_EQ(2u, verify_result.verified_cert->intermediate_buffers().size());
EXPECT_EQ(intermediate->GetCertBuffer(),
verify_result.verified_cert->intermediate_buffers()[0].get());
EXPECT_EQ(root->GetCertBuffer(),
verify_result.verified_cert->intermediate_buffers()[1].get());
}
}
TEST_F(CertVerifyProcBuiltin2QwacTest, TwoQwacRequiresPolicies) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
intermediate->SetCertificatePolicies({"2.5.29.32.0"});
leaf->SetQwacQcStatements({bssl::der::Input(kEtsiQctWebOid)});
leaf->SetExtendedKeyUsages({bssl::der::Input(kIdKpTlsBinding)});
InitializeVerifyProc(CreateParams({}));
AddMockEutlRoot(root->GetCertBuffer());
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
int error = Verify2Qwac(leaf->GetX509CertificateChain(), "www.example.com",
&verify_result, &verify_net_log_source);
EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
ExpectHistogramSample(histograms,
Verify2QwacBindingResult::kCertInconsistentBits);
}
leaf->SetCertificatePolicies({"0.4.0.194112.1.6"});
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
int error = Verify2Qwac(leaf->GetX509CertificateChain(), "www.example.com",
&verify_result, &verify_net_log_source);
EXPECT_THAT(error, IsOk());
EXPECT_FALSE(IsCertStatusError(verify_result.cert_status));
ExpectNoHistogramSample(histograms);
}
}
TEST_F(CertVerifyProcBuiltin2QwacTest, TwoQwacRequiresQcStatements) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
intermediate->SetCertificatePolicies({"2.5.29.32.0"});
leaf->SetCertificatePolicies({"0.4.0.194112.1.6"});
leaf->SetExtendedKeyUsages({bssl::der::Input(kIdKpTlsBinding)});
InitializeVerifyProc(CreateParams({}));
AddMockEutlRoot(root->GetCertBuffer());
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
int error = Verify2Qwac(leaf->GetX509CertificateChain(), "www.example.com",
&verify_result, &verify_net_log_source);
EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
ExpectHistogramSample(histograms,
Verify2QwacBindingResult::kCertInconsistentBits);
}
leaf->SetQwacQcStatements({bssl::der::Input(kEtsiQctWebOid)});
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
int error = Verify2Qwac(leaf->GetX509CertificateChain(), "www.example.com",
&verify_result, &verify_net_log_source);
EXPECT_THAT(error, IsOk());
EXPECT_FALSE(IsCertStatusError(verify_result.cert_status));
ExpectNoHistogramSample(histograms);
}
}
TEST_F(CertVerifyProcBuiltin2QwacTest, TwoQwacRequiresEku) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
intermediate->SetCertificatePolicies({"2.5.29.32.0"});
leaf->SetCertificatePolicies({"0.4.0.194112.1.6"});
leaf->SetQwacQcStatements({bssl::der::Input(kEtsiQctWebOid)});
InitializeVerifyProc(CreateParams({}));
AddMockEutlRoot(root->GetCertBuffer());
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
int error = Verify2Qwac(leaf->GetX509CertificateChain(), "www.example.com",
&verify_result, &verify_net_log_source);
EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
ExpectHistogramSample(histograms,
Verify2QwacBindingResult::kCertInconsistentBits);
}
leaf->SetExtendedKeyUsages({bssl::der::Input(kIdKpTlsBinding)});
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
int error = Verify2Qwac(leaf->GetX509CertificateChain(), "www.example.com",
&verify_result, &verify_net_log_source);
EXPECT_THAT(error, IsOk());
EXPECT_FALSE(IsCertStatusError(verify_result.cert_status));
ExpectNoHistogramSample(histograms);
}
}
TEST_F(CertVerifyProcBuiltin2QwacTest, TwoQwacVerifiesName) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
intermediate->SetCertificatePolicies({"2.5.29.32.0"});
leaf->SetCertificatePolicies({"0.4.0.194112.1.6"});
leaf->SetQwacQcStatements({bssl::der::Input(kEtsiQctWebOid)});
leaf->SetExtendedKeyUsages({bssl::der::Input(kIdKpTlsBinding)});
InitializeVerifyProc(CreateParams({}));
AddMockEutlRoot(root->GetCertBuffer());
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
int error = Verify2Qwac(leaf->GetX509CertificateChain(), "www.wrong.com",
&verify_result, &verify_net_log_source);
EXPECT_THAT(error, IsError(ERR_CERT_COMMON_NAME_INVALID));
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_COMMON_NAME_INVALID);
ExpectHistogramSample(histograms,
Verify2QwacBindingResult::kCertNameInvalid);
}
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
int error = Verify2Qwac(leaf->GetX509CertificateChain(), "www.example.com",
&verify_result, &verify_net_log_source);
EXPECT_THAT(error, IsOk());
EXPECT_FALSE(IsCertStatusError(verify_result.cert_status));
ExpectNoHistogramSample(histograms);
}
}
TEST_F(CertVerifyProcBuiltin2QwacTest, TwoQwacVerifiesValidityDate) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
intermediate->SetCertificatePolicies({"2.5.29.32.0"});
leaf->SetCertificatePolicies({"0.4.0.194112.1.6"});
leaf->SetQwacQcStatements({bssl::der::Input(kEtsiQctWebOid)});
leaf->SetExtendedKeyUsages({bssl::der::Input(kIdKpTlsBinding)});
leaf->SetValidity(base::Time::Now() - base::Days(2),
base::Time::Now() - base::Days(1));
InitializeVerifyProc(CreateParams({}));
AddMockEutlRoot(root->GetCertBuffer());
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
int error = Verify2Qwac(leaf->GetX509CertificateChain(), "www.example.com",
&verify_result, &verify_net_log_source);
EXPECT_THAT(error, IsError(ERR_CERT_DATE_INVALID));
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_DATE_INVALID);
ExpectHistogramSample(histograms,
Verify2QwacBindingResult::kCertDateInvalid);
}
leaf->SetValidity(base::Time::Now() - base::Days(2),
base::Time::Now() + base::Days(3650));
{
base::HistogramTester histograms;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
int error = Verify2Qwac(leaf->GetX509CertificateChain(), "www.example.com",
&verify_result, &verify_net_log_source);
EXPECT_THAT(error, IsOk());
EXPECT_FALSE(IsCertStatusError(verify_result.cert_status));
ExpectNoHistogramSample(histograms);
}
}
class CertVerifyProcBuiltin2QwacBindingTest : public CertVerifyProcBuiltinTest {
public:
void ExpectHistogramSample(const base::HistogramTester& histograms,
Verify2QwacBindingResult result) {
histograms.ExpectUniqueSample("Net.CertVerifier.Qwac.2QwacBinding", result,
1u);
}
};
TEST_F(CertVerifyProcBuiltin2QwacBindingTest, TestValidBinding) {
auto [tls_leaf, tls_root] = CertBuilder::CreateSimpleChain2();
TwoQwacCertBindingBuilder binding_builder;
binding_builder.SetBoundCerts({tls_leaf->GetDER()});
std::string jws = binding_builder.GetJWS();
InitializeVerifyProc(CreateParams({}));
AddMockEutlRoot(binding_builder.GetRootBuilder()->GetCertBuffer());
base::HistogramTester histograms;
RecordingNetLogObserver net_log_observer(NetLogCaptureMode::kDefault);
NetLogSource verify_net_log_source;
scoped_refptr<X509Certificate> verified_2qwac = Verify2QwacBinding(
jws, "www.example.com", base::as_byte_span(tls_leaf->GetDER()),
&verify_net_log_source);
ASSERT_TRUE(verified_2qwac);
EXPECT_TRUE(verified_2qwac->EqualsIncludingChain(
binding_builder.GetLeafBuilder()->GetX509CertificateFullChain().get()));
ExpectHistogramSample(histograms,
Verify2QwacBindingResult::kValid2QwacBinding);
auto events = net_log_observer.GetEntriesForSource(verify_net_log_source);
auto event =
std::ranges::find(events, NetLogEventType::CERT_VERIFY_PROC_2QWAC_BINDING,
&NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::BEGIN, event->phase);
EXPECT_EQ(jws, base::optional_ref(event->params.FindString("binding")));
EXPECT_EQ("www.example.com",
base::optional_ref(event->params.FindString("host")));
EXPECT_EQ(base::as_byte_span(tls_leaf->GetDER()),
ParsePemCertificate(event->params.FindString("tls_certificate")));
event = std::ranges::find(++event, events.end(),
NetLogEventType::CERT_VERIFY_PROC_2QWAC,
&NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::BEGIN, event->phase);
event = std::ranges::find(++event, events.end(),
NetLogEventType::CERT_VERIFY_PROC_2QWAC,
&NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::END, event->phase);
EXPECT_FALSE(event->params.Find("net_error"));
base::Value::Dict* pem_verified_certs =
event->params.FindDict("verified_cert");
ASSERT_TRUE(pem_verified_certs);
EXPECT_THAT(ParseNetLogCertificatesDict(*pem_verified_certs),
testing::ElementsAre(binding_builder.GetLeafBuilder()->GetDER(),
binding_builder.GetRootBuilder()->GetDER()));
event = std::ranges::find(++event, events.end(),
NetLogEventType::CERT_VERIFY_PROC_2QWAC_BINDING,
&NetLogEntry::type);
ASSERT_NE(event, events.end());
EXPECT_EQ(net::NetLogEventPhase::END, event->phase);
EXPECT_FALSE(event->params.Find("net_error"));
EXPECT_EQ(true, event->params.FindBool("is_valid_2qwac_binding"));
}
TEST_F(CertVerifyProcBuiltin2QwacBindingTest, TestBindingFailsParsing) {
auto [tls_leaf, tls_root] = CertBuilder::CreateSimpleChain2();
TwoQwacCertBindingBuilder binding_builder;
binding_builder.SetBoundCerts({tls_leaf->GetDER()});
std::string jws = "invalid:" + binding_builder.GetJWS();
InitializeVerifyProc(CreateParams({}));
AddMockEutlRoot(binding_builder.GetRootBuilder()->GetCertBuffer());
RecordingNetLogObserver net_log_observer(NetLogCaptureMode::kDefault);
base::HistogramTester histograms;
NetLogSource verify_net_log_source;
EXPECT_FALSE(Verify2QwacBinding(jws, "www.example.com",
base::as_byte_span(tls_leaf->GetDER()),
&verify_net_log_source));
ExpectHistogramSample(histograms,
Verify2QwacBindingResult::kBindingParsingError);
auto end_events = net_log_observer.GetEntriesForSourceWithType(
verify_net_log_source, NetLogEventType::CERT_VERIFY_PROC_2QWAC_BINDING,
net::NetLogEventPhase::END);
ASSERT_EQ(1U, end_events.size());
auto& event = end_events[0];
EXPECT_EQ(ERR_FAILED, event.params.FindInt("net_error"));
EXPECT_EQ("binding parsing error: base64 decoding header error",
base::optional_ref(event.params.FindString("error_description")));
}
TEST_F(CertVerifyProcBuiltin2QwacBindingTest, TestBindingInvalidSignature) {
auto [tls_leaf, tls_root] = CertBuilder::CreateSimpleChain2();
TwoQwacCertBindingBuilder binding_builder;
binding_builder.SetBoundCerts({tls_leaf->GetDER()});
std::string jws = binding_builder.GetJWSWithInvalidSignature();
InitializeVerifyProc(CreateParams({}));
AddMockEutlRoot(binding_builder.GetRootBuilder()->GetCertBuffer());
RecordingNetLogObserver net_log_observer(NetLogCaptureMode::kDefault);
base::HistogramTester histograms;
NetLogSource verify_net_log_source;
EXPECT_FALSE(Verify2QwacBinding(jws, "www.example.com",
base::as_byte_span(tls_leaf->GetDER()),
&verify_net_log_source));
ExpectHistogramSample(histograms,
Verify2QwacBindingResult::kBindingSignatureInvalid);
auto end_events = net_log_observer.GetEntriesForSourceWithType(
verify_net_log_source, NetLogEventType::CERT_VERIFY_PROC_2QWAC_BINDING,
net::NetLogEventPhase::END);
ASSERT_EQ(1U, end_events.size());
auto& event = end_events[0];
EXPECT_EQ(ERR_FAILED, event.params.FindInt("net_error"));
EXPECT_EQ("binding signature invalid",
base::optional_ref(event.params.FindString("error_description")));
}
TEST_F(CertVerifyProcBuiltin2QwacBindingTest,
TestBinding2QwacFailsVerification) {
auto [tls_leaf, tls_root] = CertBuilder::CreateSimpleChain2();
TwoQwacCertBindingBuilder binding_builder;
binding_builder.SetBoundCerts({tls_leaf->GetDER()});
std::string jws = binding_builder.GetJWS();
InitializeVerifyProc(CreateParams({}));
RecordingNetLogObserver net_log_observer(NetLogCaptureMode::kDefault);
base::HistogramTester histograms;
NetLogSource verify_net_log_source;
EXPECT_FALSE(Verify2QwacBinding(jws, "www.example.com",
base::as_byte_span(tls_leaf->GetDER()),
&verify_net_log_source));
ExpectHistogramSample(histograms,
Verify2QwacBindingResult::kCertAuthorityInvalid);
auto end_events = net_log_observer.GetEntriesForSourceWithType(
verify_net_log_source, NetLogEventType::CERT_VERIFY_PROC_2QWAC_BINDING,
net::NetLogEventPhase::END);
ASSERT_EQ(1U, end_events.size());
auto& event = end_events[0];
EXPECT_EQ(ERR_FAILED, event.params.FindInt("net_error"));
EXPECT_EQ("2-QWAC cert verify failed",
base::optional_ref(event.params.FindString("error_description")));
}
TEST_F(CertVerifyProcBuiltin2QwacBindingTest, TestTlsCertIsNotBound) {
auto [bound_leaf, bound_root] = CertBuilder::CreateSimpleChain2();
auto [tls_leaf, tls_root] = CertBuilder::CreateSimpleChain2();
TwoQwacCertBindingBuilder binding_builder;
binding_builder.SetBoundCerts({bound_leaf->GetDER()});
std::string jws = binding_builder.GetJWS();
InitializeVerifyProc(CreateParams({}));
AddMockEutlRoot(binding_builder.GetRootBuilder()->GetCertBuffer());
RecordingNetLogObserver net_log_observer(NetLogCaptureMode::kDefault);
base::HistogramTester histograms;
NetLogSource verify_net_log_source;
EXPECT_FALSE(Verify2QwacBinding(jws, "www.example.com",
base::as_byte_span(tls_leaf->GetDER()),
&verify_net_log_source));
ExpectHistogramSample(histograms, Verify2QwacBindingResult::kTlsCertNotBound);
auto end_events = net_log_observer.GetEntriesForSourceWithType(
verify_net_log_source, NetLogEventType::CERT_VERIFY_PROC_2QWAC_BINDING,
net::NetLogEventPhase::END);
ASSERT_EQ(1U, end_events.size());
auto& event = end_events[0];
EXPECT_EQ(ERR_FAILED, event.params.FindInt("net_error"));
EXPECT_EQ("TLS cert not bound",
base::optional_ref(event.params.FindString("error_description")));
}
#endif
TEST_F(CertVerifyProcBuiltinTest, DeadlineExceededDuringSyncGetIssuers) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
InitializeVerifyProc(CreateParams(
{root->GetX509Certificate()}));
BlockingTrustStore trust_store;
AddTrustStore(&trust_store);
auto intermediate_parsed_cert = bssl::ParsedCertificate::Create(
intermediate->DupCertBuffer(), {}, nullptr);
ASSERT_TRUE(intermediate_parsed_cert);
trust_store.backing_trust_store_.AddCertificateWithUnspecifiedTrust(
intermediate_parsed_cert);
scoped_refptr<X509Certificate> chain = leaf->GetX509Certificate();
ASSERT_TRUE(chain.get());
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback verify_callback;
Verify(chain.get(), "www.example.com",
0,
&verify_result, &verify_net_log_source, verify_callback.callback());
trust_store.sync_get_issuer_started_event_.Wait();
const base::TimeDelta timeout_increment =
GetCertVerifyProcBuiltinTimeLimitForTesting() + base::Milliseconds(1);
task_environment().AdvanceClock(timeout_increment);
trust_store.sync_get_issuer_ok_to_finish_event_.Signal();
int error = verify_callback.WaitForResult();
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
ASSERT_EQ(1u, verify_result.verified_cert->intermediate_buffers().size());
EXPECT_EQ(intermediate->GetCertBuffer(),
verify_result.verified_cert->intermediate_buffers()[0].get());
}
namespace {
std::string UnknownSignatureAlgorithmTLV() {
const uint8_t kInvalidSignatureAlgorithmTLV[] = {
0x30, 0x10, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x00, 0x05, 0x00};
return std::string(std::begin(kInvalidSignatureAlgorithmTLV),
std::end(kInvalidSignatureAlgorithmTLV));
}
std::string InvalidSignatureAlgorithmTLV() {
const uint8_t kInvalidSignatureAlgorithmTLV[] = {0x30, 0x03, 0x02, 0x01,
0x2a};
return std::string(std::begin(kInvalidSignatureAlgorithmTLV),
std::end(kInvalidSignatureAlgorithmTLV));
}
}
TEST_F(CertVerifyProcBuiltinTest, UnknownSignatureAlgorithmTarget) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
leaf->SetSignatureAlgorithmTLV(UnknownSignatureAlgorithmTLV());
ScopedTestRoot scoped_root(root->GetX509Certificate());
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
int flags = 0;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", flags, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
}
TEST_F(CertVerifyProcBuiltinTest,
UnparsableMismatchedTBSSignatureAlgorithmTarget) {
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
leaf->SetTBSSignatureAlgorithmTLV(InvalidSignatureAlgorithmTLV());
ScopedTestRoot scoped_root(root->GetX509Certificate());
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
int flags = 0;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", flags, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
}
TEST_F(CertVerifyProcBuiltinTest, UnknownSignatureAlgorithmIntermediate) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
intermediate->SetSignatureAlgorithmTLV(UnknownSignatureAlgorithmTLV());
ScopedTestRoot scoped_root(root->GetX509Certificate());
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
int flags = 0;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", flags, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
}
TEST_F(CertVerifyProcBuiltinTest,
UnparsableMismatchedTBSSignatureAlgorithmIntermediate) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
intermediate->SetTBSSignatureAlgorithmTLV(InvalidSignatureAlgorithmTLV());
ScopedTestRoot scoped_root(root->GetX509Certificate());
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
ASSERT_EQ(chain->intermediate_buffers().size(), 1U);
int flags = 0;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", flags, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
}
TEST_F(CertVerifyProcBuiltinTest, UnknownSignatureAlgorithmRoot) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
root->SetSignatureAlgorithmTLV(UnknownSignatureAlgorithmTLV());
ScopedTestRoot scoped_root(root->GetX509Certificate());
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
int flags = 0;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", flags, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_UnparsableMismatchedTBSSignatureAlgorithmRoot \
DISABLED_UnparsableMismatchedTBSSignatureAlgorithmRoot
#else
#define MAYBE_UnparsableMismatchedTBSSignatureAlgorithmRoot \
UnparsableMismatchedTBSSignatureAlgorithmRoot
#endif
TEST_F(CertVerifyProcBuiltinTest,
MAYBE_UnparsableMismatchedTBSSignatureAlgorithmRoot) {
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
root->SetTBSSignatureAlgorithmTLV(InvalidSignatureAlgorithmTLV());
ScopedTestRoot scoped_root(root->GetX509Certificate());
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
ASSERT_TRUE(chain.get());
int flags = 0;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", flags, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_THAT(error, IsOk());
}
TEST_F(CertVerifyProcBuiltinTest, IterationLimit) {
std::vector<std::unique_ptr<CertBuilder>> builders =
CertBuilder::CreateSimpleChain(6);
base::Time not_before = base::Time::Now() - base::Days(1);
base::Time not_after = base::Time::Now() + base::Days(1);
for (auto& builder : builders) {
builder->SetValidity(not_before, not_after);
}
std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
for (size_t i = 1; i < builders.size(); i++) {
intermediates.push_back(builders[i]->DupCertBuffer());
builders[i]->SetValidity(not_before, not_after + base::Seconds(1));
intermediates.push_back(builders[i]->DupCertBuffer());
}
CertBuilder root_ok(builders[2]->GetCertBuffer(),
nullptr);
CertBuilder intermediate_ok(builders[1]->GetCertBuffer(),
&root_ok);
intermediate_ok.SetSubjectTLV(base::as_byte_span(builders[1]->GetSubject()));
intermediate_ok.SetKey(bssl::UpRef(builders[1]->GetKey()));
intermediate_ok.SetSubjectKeyIdentifier(
builders[1]->GetSubjectKeyIdentifier());
intermediate_ok.SetValidity(not_before - base::Seconds(10),
not_after - base::Seconds(10));
intermediates.push_back(intermediate_ok.DupCertBuffer());
ScopedTestRoot scoped_root(root_ok.GetX509Certificate().get());
scoped_refptr<X509Certificate> chain = X509Certificate::CreateFromBuffer(
builders[0]->DupCertBuffer(), std::move(intermediates));
ASSERT_TRUE(chain.get());
RecordingNetLogObserver net_log_observer(NetLogCaptureMode::kDefault);
int flags = 0;
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(chain.get(), "www.example.com", flags, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
auto events = net_log_observer.GetEntriesForSource(verify_net_log_source);
auto event = std::ranges::find_if(events, [](const NetLogEntry& e) {
return e.type == NetLogEventType::CERT_VERIFY_PROC_PATH_BUILD_ATTEMPT &&
e.phase == NetLogEventPhase::END;
});
ASSERT_NE(event, events.end());
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_AUTHORITY_INVALID);
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
EXPECT_EQ(true, event->params.FindBool("exceeded_iteration_limit"));
}
class CertVerifyProcBuiltinSelfSignedTest
: public CertVerifyProcBuiltinTest,
public testing::WithParamInterface<bool> {
public:
CertVerifyProcBuiltinSelfSignedTest() {
if (GetParam()) {
feature_list_.InitAndEnableFeature(
features::kSelfSignedLocalNetworkInterstitial);
} else {
feature_list_.InitAndDisableFeature(
features::kSelfSignedLocalNetworkInterstitial);
}
}
scoped_refptr<X509Certificate> CreateSelfSigned(
std::string_view subject_dns_name) {
std::vector<std::unique_ptr<CertBuilder>> builders =
CertBuilder::CreateSimpleChain(1);
base::Time not_before = base::Time::Now() - base::Days(1);
base::Time not_after = base::Time::Now() + base::Days(1);
builders[0]->SetValidity(not_before, not_after);
builders[0]->SetSubjectAltName(subject_dns_name);
return builders[0]->GetX509Certificate();
}
scoped_refptr<X509Certificate> CreateSelfSignedIPSubject(
std::string_view ip_address) {
std::vector<std::unique_ptr<CertBuilder>> builders =
CertBuilder::CreateSimpleChain(1);
base::Time not_before = base::Time::Now() - base::Days(1);
base::Time not_after = base::Time::Now() + base::Days(1);
builders[0]->SetValidity(not_before, not_after);
IPAddress ip;
if (!ParseURLHostnameToAddress(ip_address, &ip)) {
ADD_FAILURE() << "Failed to parse IP address";
}
builders[0]->SetSubjectAltNames({}, {ip});
return builders[0]->GetX509Certificate();
}
private:
base::test::ScopedFeatureList feature_list_;
};
TEST_P(CertVerifyProcBuiltinSelfSignedTest,
SelfSignedCertOnLocalNetworkHostname) {
scoped_refptr<X509Certificate> cert = CreateSelfSigned("testurl.local");
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(cert, "testurl.local", 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
if (GetParam()) {
EXPECT_TRUE(verify_result.cert_status &
CERT_STATUS_SELF_SIGNED_LOCAL_NETWORK);
EXPECT_THAT(error, IsError(ERR_CERT_SELF_SIGNED_LOCAL_NETWORK));
} else {
EXPECT_FALSE(verify_result.cert_status &
CERT_STATUS_SELF_SIGNED_LOCAL_NETWORK);
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
}
TEST_P(CertVerifyProcBuiltinSelfSignedTest, SelfSignedCertOnLocalNetworkIP) {
scoped_refptr<X509Certificate> cert =
CreateSelfSignedIPSubject("192.168.0.1");
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(cert, "192.168.0.1", 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
if (GetParam()) {
EXPECT_TRUE(verify_result.cert_status &
CERT_STATUS_SELF_SIGNED_LOCAL_NETWORK);
EXPECT_THAT(error, IsError(ERR_CERT_SELF_SIGNED_LOCAL_NETWORK));
} else {
EXPECT_FALSE(verify_result.cert_status &
CERT_STATUS_SELF_SIGNED_LOCAL_NETWORK);
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
}
TEST_P(CertVerifyProcBuiltinSelfSignedTest, SelfSignedCertOnLocalNetworkIPv6) {
scoped_refptr<X509Certificate> cert =
CreateSelfSignedIPSubject("[fc00:0:0:0:0:0:0:0]");
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(cert, "fc00:0:0:0:0:0:0:0", 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
if (GetParam()) {
EXPECT_TRUE(verify_result.cert_status &
CERT_STATUS_SELF_SIGNED_LOCAL_NETWORK);
EXPECT_THAT(error, IsError(ERR_CERT_SELF_SIGNED_LOCAL_NETWORK));
} else {
EXPECT_FALSE(verify_result.cert_status &
CERT_STATUS_SELF_SIGNED_LOCAL_NETWORK);
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
}
TEST_P(CertVerifyProcBuiltinSelfSignedTest, NonSelfSignedCertOnLocalNetwork) {
std::vector<std::unique_ptr<CertBuilder>> builders =
CertBuilder::CreateSimpleChain(2);
base::Time not_before = base::Time::Now() - base::Days(2);
base::Time not_after = base::Time::Now() - base::Days(2);
builders[0]->SetValidity(not_before, not_after);
builders[0]->SetSubjectAltName("testurl.local");
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(builders[0]->GetX509CertificateChain(), "testurl.local", 0,
&verify_result, &verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_FALSE(verify_result.cert_status &
CERT_STATUS_SELF_SIGNED_LOCAL_NETWORK);
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
TEST_P(CertVerifyProcBuiltinSelfSignedTest,
SelfSignedCertNotLocalNetworkHostname) {
scoped_refptr<X509Certificate> cert = CreateSelfSigned("www.example.com");
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(cert, "www.example.com", 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_FALSE(verify_result.cert_status &
CERT_STATUS_SELF_SIGNED_LOCAL_NETWORK);
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
TEST_P(CertVerifyProcBuiltinSelfSignedTest, SelfSignedCertNotLocalNetworkIP) {
scoped_refptr<X509Certificate> cert = CreateSelfSignedIPSubject("8.8.8.8");
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(cert, "8.8.8.8", 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
EXPECT_FALSE(verify_result.cert_status &
CERT_STATUS_SELF_SIGNED_LOCAL_NETWORK);
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
TEST_P(CertVerifyProcBuiltinSelfSignedTest, SelfSignedCertNotLocalNetworkIPv6) {
scoped_refptr<X509Certificate> cert =
CreateSelfSignedIPSubject("[2001:4860:4860::8888]");
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(cert, "2001:4860:4860::8888", 0, &verify_result,
&verify_net_log_source, callback.callback());
int error = callback.WaitForResult();
EXPECT_FALSE(verify_result.cert_status &
CERT_STATUS_SELF_SIGNED_LOCAL_NETWORK);
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
TEST_P(CertVerifyProcBuiltinSelfSignedTest,
SelfSignedCertOnLocalNetworkHostnameNameMismatchTakesPrecedence) {
scoped_refptr<X509Certificate> cert = CreateSelfSigned("nottesturl.local");
CertVerifyResult verify_result;
NetLogSource verify_net_log_source;
TestCompletionCallback callback;
Verify(cert, "testurl.local", 0, &verify_result, &verify_net_log_source,
callback.callback());
int error = callback.WaitForResult();
if (GetParam()) {
EXPECT_TRUE(verify_result.cert_status &
CERT_STATUS_SELF_SIGNED_LOCAL_NETWORK);
EXPECT_THAT(error, IsError(ERR_CERT_COMMON_NAME_INVALID));
} else {
EXPECT_FALSE(verify_result.cert_status &
CERT_STATUS_SELF_SIGNED_LOCAL_NETWORK);
EXPECT_THAT(error, IsError(ERR_CERT_AUTHORITY_INVALID));
}
}
INSTANTIATE_TEST_SUITE_P(SelfSignedInterstitial,
CertVerifyProcBuiltinSelfSignedTest,
testing::Bool());
}