798 lines
32 KiB
C++
798 lines
32 KiB
C++
// Copyright 2019 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "net/cert/cert_verify_proc_builtin.h"
|
|
|
|
#include "base/memory/raw_ptr.h"
|
|
#include "base/numerics/safe_conversions.h"
|
|
#include "base/ranges/algorithm.h"
|
|
#include "base/run_loop.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/task_environment.h"
|
|
#include "base/time/time.h"
|
|
#include "net/base/test_completion_callback.h"
|
|
#include "net/cert/cert_verify_proc.h"
|
|
#include "net/cert/crl_set.h"
|
|
#include "net/cert/ev_root_ca_metadata.h"
|
|
#include "net/cert/internal/system_trust_store.h"
|
|
#include "net/cert/pki/trust_store.h"
|
|
#include "net/cert/pki/trust_store_collection.h"
|
|
#include "net/cert/pki/trust_store_in_memory.h"
|
|
#include "net/cert_net/cert_net_fetcher_url_request.h"
|
|
#include "net/der/encode_values.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/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"
|
|
|
|
using net::test::IsError;
|
|
using net::test::IsOk;
|
|
|
|
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<char> rand_bytes;
|
|
rand_bytes.resize(num_bytes);
|
|
|
|
base::RandBytes(rand_bytes.data(), rand_bytes.size());
|
|
return base::HexEncode(rand_bytes.data(), rand_bytes.size());
|
|
}
|
|
|
|
static std::string MakeRandomPath(base::StringPiece suffix) {
|
|
return "/" + MakeRandomHexString(12) + std::string(suffix);
|
|
}
|
|
|
|
int VerifyOnWorkerThread(const scoped_refptr<CertVerifyProc>& verify_proc,
|
|
scoped_refptr<X509Certificate> cert,
|
|
const std::string& hostname,
|
|
int flags,
|
|
const CertificateList& additional_trust_anchors,
|
|
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=*/std::string(),
|
|
/*sct_list=*/std::string(), flags,
|
|
additional_trust_anchors, verify_result, net_log);
|
|
verify_result->DetachFromSequence();
|
|
*out_source = net_log.source();
|
|
return error;
|
|
}
|
|
|
|
class MockSystemTrustStore : public SystemTrustStore {
|
|
public:
|
|
TrustStore* GetTrustStore() override { return &trust_store_; }
|
|
|
|
bool UsesSystemTrustStore() const override { return false; }
|
|
|
|
bool IsKnownRoot(const ParsedCertificate* trust_anchor) const override {
|
|
return mock_is_known_root_;
|
|
}
|
|
|
|
void AddTrustStore(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)
|
|
int64_t chrome_root_store_version() override { return 0; }
|
|
#endif
|
|
|
|
private:
|
|
TrustStoreCollection trust_store_;
|
|
bool mock_is_known_root_ = false;
|
|
};
|
|
|
|
class BlockingTrustStore : public TrustStore {
|
|
public:
|
|
CertificateTrust GetTrust(const ParsedCertificate* cert,
|
|
base::SupportsUserData* debug_data) override {
|
|
return backing_trust_store_.GetTrust(cert, debug_data);
|
|
}
|
|
|
|
void SyncGetIssuersOf(const ParsedCertificate* cert,
|
|
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_;
|
|
TrustStoreInMemory backing_trust_store_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
class CertVerifyProcBuiltinTest : public ::testing::Test {
|
|
public:
|
|
// CertVerifyProcBuiltinTest() {}
|
|
|
|
void SetUp() override {
|
|
cert_net_fetcher_ = base::MakeRefCounted<CertNetFetcherURLRequest>();
|
|
auto mock_system_trust_store = std::make_unique<MockSystemTrustStore>();
|
|
mock_system_trust_store_ = mock_system_trust_store.get();
|
|
verify_proc_ = CreateCertVerifyProcBuiltin(
|
|
cert_net_fetcher_, CRLSet::EmptyCRLSetForTesting(),
|
|
std::move(mock_system_trust_store));
|
|
|
|
context_ = CreateTestURLRequestContextBuilder()->Build();
|
|
|
|
cert_net_fetcher_->SetURLRequestContext(context_.get());
|
|
}
|
|
|
|
void TearDown() override { cert_net_fetcher_->Shutdown(); }
|
|
|
|
void Verify(scoped_refptr<X509Certificate> cert,
|
|
const std::string& hostname,
|
|
int flags,
|
|
const CertificateList& additional_trust_anchors,
|
|
CertVerifyResult* verify_result,
|
|
NetLogSource* out_source,
|
|
CompletionOnceCallback callback) {
|
|
verify_result->DetachFromSequence();
|
|
base::ThreadPool::PostTaskAndReplyWithResult(
|
|
FROM_HERE,
|
|
{base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
|
|
base::BindOnce(&VerifyOnWorkerThread, verify_proc_, std::move(cert),
|
|
hostname, flags, additional_trust_anchors, verify_result,
|
|
out_source),
|
|
std::move(callback));
|
|
}
|
|
|
|
base::test::TaskEnvironment& task_environment() { return task_environment_; }
|
|
|
|
// Creates a CRL issued and signed by |crl_issuer|, marking |revoked_serials|
|
|
// as revoked, and registers it to be served by the test server.
|
|
// Returns the full URL to retrieve the CRL from the test server.
|
|
GURL CreateAndServeCrl(
|
|
EmbeddedTestServer* test_server,
|
|
CertBuilder* crl_issuer,
|
|
const std::vector<uint64_t>& revoked_serials,
|
|
absl::optional<SignatureAlgorithm> signature_algorithm = absl::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(TrustStore* store) {
|
|
mock_system_trust_store_->AddTrustStore(store);
|
|
}
|
|
|
|
void SetMockIsKnownRoot(bool is_known_root) {
|
|
mock_system_trust_store_->SetMockIsKnownRoot(is_known_root);
|
|
}
|
|
|
|
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_;
|
|
|
|
// Must outlive `mock_system_trust_store_`.
|
|
scoped_refptr<CertVerifyProc> verify_proc_;
|
|
|
|
raw_ptr<MockSystemTrustStore> mock_system_trust_store_ = nullptr;
|
|
scoped_refptr<CertNetFetcherURLRequest> cert_net_fetcher_;
|
|
};
|
|
|
|
TEST_F(CertVerifyProcBuiltinTest, SimpleSuccess) {
|
|
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
|
|
|
|
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
|
|
ASSERT_TRUE(chain.get());
|
|
|
|
base::HistogramTester histogram_tester;
|
|
CertVerifyResult verify_result;
|
|
NetLogSource verify_net_log_source;
|
|
TestCompletionCallback callback;
|
|
Verify(chain.get(), "www.example.com", /*flags=*/0,
|
|
/*additional_trust_anchors=*/{root->GetX509Certificate()},
|
|
&verify_result, &verify_net_log_source, callback.callback());
|
|
|
|
int error = callback.WaitForResult();
|
|
EXPECT_THAT(error, IsOk());
|
|
EXPECT_THAT(histogram_tester.GetAllSamples(
|
|
"Net.CertVerifier.PathBuilderIterationCount"),
|
|
testing::ElementsAre(base::Bucket(/*min=*/2, /*count=*/1)));
|
|
}
|
|
|
|
TEST_F(CertVerifyProcBuiltinTest, CRLNotCheckedForKnownRoots) {
|
|
auto [leaf, root] = CertBuilder::CreateSimpleChain2();
|
|
|
|
EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTP);
|
|
ASSERT_TRUE(test_server.InitializeAndListen());
|
|
|
|
// CRL that marks leaf as revoked.
|
|
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,
|
|
/*additional_trust_anchors=*/{root->GetX509Certificate()},
|
|
&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);
|
|
}
|
|
|
|
{
|
|
// Pretend the root is a known root.
|
|
SetMockIsKnownRoot(true);
|
|
base::HistogramTester histogram_tester;
|
|
CertVerifyResult verify_result;
|
|
TestCompletionCallback verify_callback;
|
|
Verify(chain.get(), "www.example.com",
|
|
CertVerifyProc::VERIFY_REV_CHECKING_ENABLED,
|
|
/*additional_trust_anchors=*/{root->GetX509Certificate()},
|
|
&verify_result, &verify_net_log_source, verify_callback.callback());
|
|
|
|
int error = verify_callback.WaitForResult();
|
|
// CRLs are not checked for chains issued by known roots, so verification
|
|
// should be successful.
|
|
EXPECT_THAT(error, IsOk());
|
|
EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
|
|
EXPECT_THAT(histogram_tester.GetAllSamples(
|
|
"Net.CertVerifier.PathBuilderIterationCount"),
|
|
testing::ElementsAre(base::Bucket(/*min=*/1, /*count=*/1)));
|
|
}
|
|
}
|
|
|
|
// Tests that if the verification deadline is exceeded during revocation
|
|
// checking, additional CRL fetches will not be attempted.
|
|
TEST_F(CertVerifyProcBuiltinTest, RevocationCheckDeadlineCRL) {
|
|
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
|
|
|
|
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());
|
|
|
|
// Set up the test cert to have enough crlDistributionPoint urls that if the
|
|
// first N-1 requests hang the deadline will be exceeded before the Nth
|
|
// request is made.
|
|
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())));
|
|
}
|
|
// Add CRL URLs and handlers that will add test failures if requested.
|
|
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());
|
|
|
|
base::HistogramTester histogram_tester;
|
|
CertVerifyResult verify_result;
|
|
NetLogSource verify_net_log_source;
|
|
TestCompletionCallback verify_callback;
|
|
Verify(chain.get(), "www.example.com",
|
|
CertVerifyProc::VERIFY_REV_CHECKING_ENABLED,
|
|
/*additional_trust_anchors=*/{root->GetX509Certificate()},
|
|
&verify_result, &verify_net_log_source, verify_callback.callback());
|
|
|
|
for (int i = 0; i < expected_request_count; i++) {
|
|
// Wait for request #|i| to be made.
|
|
runloops[i].Run();
|
|
// Advance virtual time to cause the timeout task to become runnable.
|
|
task_environment().AdvanceClock(timeout_increment);
|
|
}
|
|
|
|
// Once |expected_request_count| requests have been made and timed out, the
|
|
// overall deadline should be reached, and no more requests should have been
|
|
// made. (If they were, the test will fail due to the ADD_FAILURE callback in
|
|
// the request handlers.)
|
|
int error = verify_callback.WaitForResult();
|
|
// Soft-fail revocation checking was used, therefore verification result
|
|
// should be OK even though none of the CRLs could be retrieved.
|
|
EXPECT_THAT(error, IsOk());
|
|
EXPECT_THAT(histogram_tester.GetAllSamples(
|
|
"Net.CertVerifier.PathBuilderIterationCount"),
|
|
testing::ElementsAre(base::Bucket(/*min=*/2, /*count=*/1)));
|
|
}
|
|
|
|
// Tests that if the verification deadline is exceeded during revocation
|
|
// checking, additional OCSP fetches will not be attempted.
|
|
TEST_F(CertVerifyProcBuiltinTest, RevocationCheckDeadlineOCSP) {
|
|
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
|
|
|
|
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());
|
|
|
|
// Set up the test cert to have enough OCSP urls that if the
|
|
// first N-1 requests hang the deadline will be exceeded before the Nth
|
|
// request is made.
|
|
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())));
|
|
}
|
|
// Add OCSP URLs and handlers that will add test failures if requested.
|
|
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,
|
|
/*additional_trust_anchors=*/{root->GetX509Certificate()},
|
|
&verify_result, &verify_net_log_source, verify_callback.callback());
|
|
|
|
for (int i = 0; i < expected_request_count; i++) {
|
|
// Wait for request #|i| to be made.
|
|
runloops[i].Run();
|
|
// Advance virtual time to cause the timeout task to become runnable.
|
|
task_environment().AdvanceClock(timeout_increment);
|
|
}
|
|
|
|
// Once |expected_request_count| requests have been made and timed out, the
|
|
// overall deadline should be reached, and no more requests should have been
|
|
// made. (If they were, the test will fail due to the ADD_FAILURE callback in
|
|
// the request handlers.)
|
|
int error = verify_callback.WaitForResult();
|
|
// Soft-fail revocation checking was used, therefore verification result
|
|
// should be OK even though none of the OCSP responses could be retrieved.
|
|
EXPECT_THAT(error, IsOk());
|
|
}
|
|
|
|
#if defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
|
|
// Tests that if we're doing EV verification, that no OCSP revocation checking
|
|
// is done.
|
|
TEST_F(CertVerifyProcBuiltinTest, EVNoOCSPRevocationChecks) {
|
|
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
|
|
|
|
// Add test EV policy to leaf and intermediate.
|
|
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());
|
|
|
|
// Set up the test intermediate to have an OCSP url that fails the test if
|
|
// called.
|
|
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();
|
|
|
|
// Consider the root of the test chain a valid EV root for the test policy.
|
|
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",
|
|
/*flags=*/0,
|
|
/*additional_trust_anchors=*/{root->GetX509Certificate()},
|
|
&verify_result, &verify_net_log_source, verify_callback.callback());
|
|
|
|
// EV doesn't do revocation checking, therefore verification result
|
|
// should be OK and EV.
|
|
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 = base::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 = base::ranges::find(++event, events.end(),
|
|
NetLogEventType::CERT_VERIFY_PROC_PATH_BUILT,
|
|
&NetLogEntry::type);
|
|
ASSERT_NE(event, events.end());
|
|
EXPECT_EQ(net::NetLogEventPhase::NONE, event->phase);
|
|
EXPECT_FALSE(event->params.FindString("errors"));
|
|
|
|
event = base::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 // defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
|
|
|
|
TEST_F(CertVerifyProcBuiltinTest, DeadlineExceededDuringSyncGetIssuers) {
|
|
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
|
|
|
|
BlockingTrustStore trust_store;
|
|
AddTrustStore(&trust_store);
|
|
|
|
auto intermediate_parsed_cert =
|
|
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",
|
|
/*flags=*/0,
|
|
/*additional_trust_anchors=*/{root->GetX509Certificate()},
|
|
&verify_result, &verify_net_log_source, verify_callback.callback());
|
|
|
|
// Wait for trust_store.SyncGetIssuersOf to be called.
|
|
trust_store.sync_get_issuer_started_event_.Wait();
|
|
|
|
// Advance the clock past the verifier deadline.
|
|
const base::TimeDelta timeout_increment =
|
|
GetCertVerifyProcBuiltinTimeLimitForTesting() + base::Milliseconds(1);
|
|
task_environment().AdvanceClock(timeout_increment);
|
|
|
|
// Signal trust_store.SyncGetIssuersOf to finish.
|
|
trust_store.sync_get_issuer_ok_to_finish_event_.Signal();
|
|
|
|
int error = verify_callback.WaitForResult();
|
|
// Because the deadline was reached while retrieving the intermediate, path
|
|
// building should have stopped there and not found the root. The partial
|
|
// path built up to that point should be returned, and the error should be
|
|
// CERT_AUTHORITY_INVALID.
|
|
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());
|
|
}
|
|
|
|
TEST_F(CertVerifyProcBuiltinTest, DebugData) {
|
|
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
|
|
|
|
scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
|
|
ASSERT_TRUE(chain.get());
|
|
|
|
base::Time time = base::Time::Now();
|
|
|
|
CertVerifyResult verify_result;
|
|
NetLogSource verify_net_log_source;
|
|
TestCompletionCallback callback;
|
|
Verify(chain.get(), "www.example.com", /*flags=*/0,
|
|
/*additional_trust_anchors=*/{root->GetX509Certificate()},
|
|
&verify_result, &verify_net_log_source, callback.callback());
|
|
|
|
int error = callback.WaitForResult();
|
|
EXPECT_THAT(error, IsOk());
|
|
|
|
auto* debug_data = CertVerifyProcBuiltinResultDebugData::Get(&verify_result);
|
|
ASSERT_TRUE(debug_data);
|
|
// No delayed tasks involved, so the mock time should not have advanced.
|
|
EXPECT_EQ(time, debug_data->verification_time());
|
|
|
|
base::Time der_verification_time_converted_back_to_base_time;
|
|
EXPECT_TRUE(net::der::GeneralizedTimeToTime(
|
|
debug_data->der_verification_time(),
|
|
&der_verification_time_converted_back_to_base_time));
|
|
// GeneralizedTime only has seconds precision.
|
|
EXPECT_EQ(
|
|
0,
|
|
(time - der_verification_time_converted_back_to_base_time).InSeconds());
|
|
}
|
|
|
|
namespace {
|
|
|
|
// Returns a TLV to use as an unknown signature algorithm when building a cert.
|
|
// The specific contents are as follows (the OID is from
|
|
// https://davidben.net/oid):
|
|
//
|
|
// SEQUENCE {
|
|
// OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.0 }
|
|
// NULL {}
|
|
// }
|
|
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));
|
|
}
|
|
|
|
// Returns a TLV to use as an invalid signature algorithm when building a cert.
|
|
// This is a SEQUENCE so that it will pass the ParseCertificate code
|
|
// and fail inside ParseSignatureAlgorithm.
|
|
// SEQUENCE {
|
|
// INTEGER { 42 }
|
|
// }
|
|
std::string InvalidSignatureAlgorithmTLV() {
|
|
const uint8_t kInvalidSignatureAlgorithmTLV[] = {0x30, 0x03, 0x02, 0x01,
|
|
0x2a};
|
|
return std::string(std::begin(kInvalidSignatureAlgorithmTLV),
|
|
std::end(kInvalidSignatureAlgorithmTLV));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_F(CertVerifyProcBuiltinTest, UnknownSignatureAlgorithmTarget) {
|
|
auto [leaf, intermediate, root] = CertBuilder::CreateSimpleChain3();
|
|
leaf->SetSignatureAlgorithmTLV(UnknownSignatureAlgorithmTLV());
|
|
|
|
// Trust the root and build a chain to verify that includes the intermediate.
|
|
ScopedTestRoot scoped_root(root->GetX509Certificate().get());
|
|
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, CertificateList(),
|
|
&verify_result, &verify_net_log_source, callback.callback());
|
|
int error = callback.WaitForResult();
|
|
// Unknown signature algorithm in the leaf cert should result in the cert
|
|
// being invalid.
|
|
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();
|
|
// Set only the tbsCertificate signature to an invalid value.
|
|
leaf->SetTBSSignatureAlgorithmTLV(InvalidSignatureAlgorithmTLV());
|
|
|
|
// Trust the root and build a chain to verify.
|
|
ScopedTestRoot scoped_root(root->GetX509Certificate().get());
|
|
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, CertificateList(),
|
|
&verify_result, &verify_net_log_source, callback.callback());
|
|
int error = callback.WaitForResult();
|
|
// Invalid signature algorithm in the leaf cert should result in the
|
|
// cert being invalid.
|
|
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());
|
|
|
|
// Trust the root and build a chain to verify that includes the intermediate.
|
|
ScopedTestRoot scoped_root(root->GetX509Certificate().get());
|
|
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, CertificateList(),
|
|
&verify_result, &verify_net_log_source, callback.callback());
|
|
int error = callback.WaitForResult();
|
|
// Unknown signature algorithm in the intermediate cert should result in the
|
|
// cert being invalid.
|
|
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();
|
|
// Set only the tbsCertificate signature to an invalid value.
|
|
intermediate->SetTBSSignatureAlgorithmTLV(InvalidSignatureAlgorithmTLV());
|
|
|
|
// Trust the root and build a chain to verify that includes the intermediate.
|
|
ScopedTestRoot scoped_root(root->GetX509Certificate().get());
|
|
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, CertificateList(),
|
|
&verify_result, &verify_net_log_source, callback.callback());
|
|
int error = callback.WaitForResult();
|
|
// Invalid signature algorithm in the intermediate cert should result in the
|
|
// cert being invalid.
|
|
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());
|
|
|
|
// Trust the root and build a chain to verify that includes the intermediate.
|
|
ScopedTestRoot scoped_root(root->GetX509Certificate().get());
|
|
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, CertificateList(),
|
|
&verify_result, &verify_net_log_source, callback.callback());
|
|
int error = callback.WaitForResult();
|
|
// Unknown signature algorithm in the root cert should have no effect on
|
|
// verification.
|
|
EXPECT_THAT(error, IsOk());
|
|
}
|
|
|
|
// This test is disabled on Android as adding the invalid root through
|
|
// ScopedTestRoot causes it to be parsed by the Java X509 code which barfs. We
|
|
// could re-enable if Chrome on Android has fully switched to the
|
|
// builtin-verifier and ScopedTestRoot no longer has Android-specific code.
|
|
#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();
|
|
// Set only the tbsCertificate signature to an invalid value.
|
|
root->SetTBSSignatureAlgorithmTLV(InvalidSignatureAlgorithmTLV());
|
|
|
|
// Trust the root and build a chain to verify that includes the intermediate.
|
|
ScopedTestRoot scoped_root(root->GetX509Certificate().get());
|
|
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, CertificateList(),
|
|
&verify_result, &verify_net_log_source, callback.callback());
|
|
int error = callback.WaitForResult();
|
|
// Invalid signature algorithm in the root cert should have no effect on
|
|
// verification.
|
|
EXPECT_THAT(error, IsOk());
|
|
}
|
|
|
|
} // namespace net
|