#include "services/cert_verifier/cert_verifier_service.h"
#include <stdint.h>
#include <iterator>
#include <memory>
#include <string>
#include <string_view>
#include <tuple>
#include "base/containers/adapters.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/net_errors.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/cert_verifier.h"
#include "net/cert/cert_verify_proc.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/crl_set.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "net/log/net_log.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_data_directory.h"
#include "services/cert_verifier/cert_verifier_service_factory.h"
#include "services/network/public/mojom/cert_verifier_service.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
namespace net {
class CertNetFetcher;
class ChromeRootStoreData;
}
namespace cert_verifier {
namespace {
const int kExpectedNetError = net::ERR_CERT_INVALID;
const unsigned int kExpectedCertStatus = net::CERT_STATUS_INVALID;
scoped_refptr<net::X509Certificate> GetTestCert() {
return net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem");
}
class DummyCertVerifier : public net::CertVerifierWithUpdatableProc {
public:
struct DummyRequest : public net::CertVerifier::Request {
~DummyRequest() override {
if (cancel_cb)
std::move(cancel_cb).Run();
}
net::CompletionOnceCallback callback;
raw_ptr<net::CertVerifyResult> verify_result;
base::OnceClosure cancel_cb;
base::WeakPtrFactory<DummyRequest> weak_ptr_factory_{this};
};
~DummyCertVerifier() override {
for (base::WeakPtr<DummyRequest>& dummy_req : dummy_request_ptrs) {
if (dummy_req)
dummy_req->callback.Reset();
}
}
int Verify(const net::CertVerifier::RequestParams& params,
net::CertVerifyResult* verify_result,
net::CompletionOnceCallback callback,
std::unique_ptr<Request>* out_req,
const net::NetLogWithSource& net_log) override {
request_netlogs_[params] = net_log;
if (sync_response_params_.find(params) != sync_response_params_.end()) {
verify_result->cert_status = kExpectedCertStatus;
verify_result->verified_cert = params.certificate();
return kExpectedNetError;
}
EXPECT_TRUE(dummy_requests_.find(params) == dummy_requests_.end());
auto dummy_req = std::make_unique<DummyRequest>();
dummy_request_ptrs.push_back(dummy_req->weak_ptr_factory_.GetWeakPtr());
dummy_req->verify_result = verify_result;
dummy_req->callback = std::move(callback);
dummy_req->cancel_cb = base::BindLambdaForTesting([this, params]() {
ASSERT_TRUE(cancelled_requests_.find(params) ==
cancelled_requests_.end());
cancelled_requests_.insert(std::move(params));
});
dummy_requests_[params] = dummy_req.get();
*out_req = std::move(dummy_req);
return net::ERR_IO_PENDING;
}
void Verify2QwacBinding(
const std::string& binding,
const std::string& hostname,
const scoped_refptr<net::X509Certificate>& tls_cert,
base::OnceCallback<void(const scoped_refptr<net::X509Certificate>&)>
callback,
const net::NetLogWithSource& net_log) override {
ADD_FAILURE();
std::move(callback).Run(nullptr);
}
void SetConfig(const Config& config) override { config_ = config; }
void AddObserver(Observer* observer) override {
EXPECT_FALSE(observer_);
observer_ = observer;
}
void RemoveObserver(Observer* observer) override { observer_ = nullptr; }
void UpdateVerifyProcData(
scoped_refptr<net::CertNetFetcher> cert_net_fetcher,
const net::CertVerifyProc::ImplParams& impl_params,
const net::CertVerifyProc::InstanceParams& instance_params) override {
has_updated_proc_data_ = true;
}
void RespondToRequest(const net::CertVerifier::RequestParams& params) {
auto it = dummy_requests_.find(params);
ASSERT_FALSE(it == dummy_requests_.end());
DummyRequest* req = it->second;
dummy_requests_.erase(it);
req->cancel_cb.Reset();
req->verify_result->cert_status = kExpectedCertStatus;
req->verify_result->verified_cert = params.certificate();
std::move(req->callback).Run(kExpectedNetError);
}
void ShouldRespondSynchronouslyToParams(
const net::CertVerifier::RequestParams& params) {
sync_response_params_.insert(params);
}
bool HasPendingAsyncRequest(const net::CertVerifier::RequestParams& params) {
return dummy_requests_.find(params) != dummy_requests_.end();
}
bool HasUpdatedProcData() { return has_updated_proc_data_; }
bool WasRequestCancelled(const net::CertVerifier::RequestParams& params) {
return cancelled_requests_.find(params) != cancelled_requests_.end();
}
void ExpectReceivedNetlogSource(
const net::CertVerifier::RequestParams& params,
const net::NetLogSource& net_log_source) {
ASSERT_TRUE(request_netlogs_.count(params));
EXPECT_EQ(net_log_source, request_netlogs_[params].source());
}
const net::CertVerifier::Config* config() const { return &config_; }
CertVerifier::Observer* GetObserver() { return observer_; }
private:
std::map<net::CertVerifier::RequestParams,
raw_ptr<DummyRequest, CtnExperimental>>
dummy_requests_;
std::set<net::CertVerifier::RequestParams> sync_response_params_;
std::set<net::CertVerifier::RequestParams> cancelled_requests_;
std::vector<base::WeakPtr<DummyRequest>> dummy_request_ptrs;
std::map<net::CertVerifier::RequestParams, net::NetLogWithSource>
request_netlogs_;
net::CertVerifier::Config config_;
raw_ptr<CertVerifier::Observer> observer_ = nullptr;
bool has_updated_proc_data_ = false;
};
struct DummyCVServiceRequest : public mojom::CertVerifierRequest {
void Complete(const net::CertVerifyResult& result_param,
int32_t net_error_param) override {
is_completed = true;
result = result_param;
net_error = net_error_param;
}
bool is_completed = false;
net::CertVerifyResult result;
int net_error;
};
class CertVerifierServiceTest : public PlatformTest,
public mojom::CertVerifierServiceClient {
public:
struct RequestInfo {
RequestInfo(net::CertVerifier::RequestParams request_params_p,
const net::NetLogSource& net_log_source_p,
std::unique_ptr<DummyCVServiceRequest> dummy_cv_request_p,
std::unique_ptr<mojo::Receiver<mojom::CertVerifierRequest>>
cv_request_receiver_p)
: request_params(std::move(request_params_p)),
net_log_source(net_log_source_p),
dummy_cv_request(std::move(dummy_cv_request_p)),
cv_request_receiver(std::move(cv_request_receiver_p)) {}
net::CertVerifier::RequestParams request_params;
net::NetLogSource net_log_source;
std::unique_ptr<DummyCVServiceRequest> dummy_cv_request;
std::unique_ptr<mojo::Receiver<mojom::CertVerifierRequest>>
cv_request_receiver;
};
CertVerifierServiceTest()
: cv_service_client_(this),
dummy_cv_(nullptr),
cv_service_factory_impl_(
std::make_unique<CertVerifierServiceFactoryImpl>(
cv_service_factory_remote_.BindNewPipeAndPassReceiver())) {}
void CreateImpl(bool wait_for_update) {
dummy_cv_ = new DummyCertVerifier;
auto* cvs_impl = new internal::CertVerifierServiceImpl(
base::WrapUnique(dummy_cv_.get()),
cv_service_remote_.BindNewPipeAndPassReceiver(),
cv_service_updater_remote_.BindNewPipeAndPassReceiver(),
cv_service_client_.BindNewPipeAndPassRemote(),
nullptr,
{},
wait_for_update);
cvs_impl->SetCertVerifierServiceFactory(
cv_service_factory_impl_->GetWeakPtr());
}
void SetUp() override { ASSERT_TRUE(GetTestCert()); }
void TestCompletions(int num_simultaneous, bool sync) {
std::vector<RequestInfo> request_infos;
base::TimeTicks time_zero = base::TimeTicks::Now();
for (int i = 0; i < num_simultaneous; i++) {
std::string hostname = "www" + base::NumberToString(i) + ".example.com";
net::CertVerifier::RequestParams dummy_params(
GetTestCert(), hostname, 0,
std::string(),
std::string());
if (sync) {
dummy_cv_->ShouldRespondSynchronouslyToParams(dummy_params);
}
auto cv_service_req = std::make_unique<DummyCVServiceRequest>();
auto cv_request_receiver =
std::make_unique<mojo::Receiver<mojom::CertVerifierRequest>>(
cv_service_req.get());
request_infos.emplace_back(
std::move(dummy_params),
net::NetLogSource(net::NetLogSourceType::CERT_VERIFIER_JOB, 1234 + i,
time_zero + base::Seconds(i)),
std::move(cv_service_req), std::move(cv_request_receiver));
}
for (RequestInfo& info : request_infos) {
cv_service_remote_->Verify(
info.request_params, info.net_log_source,
info.cv_request_receiver->BindNewPipeAndPassRemote());
}
task_environment_.RunUntilIdle();
if (!sync) {
for (auto& info : base::Reversed(request_infos)) {
ASSERT_FALSE(info.dummy_cv_request->is_completed);
dummy_cv()->RespondToRequest(info.request_params);
}
cv_service_remote().FlushForTesting();
}
for (RequestInfo& info : request_infos) {
ASSERT_TRUE(info.dummy_cv_request->is_completed);
ASSERT_EQ(info.dummy_cv_request->net_error, kExpectedNetError);
ASSERT_EQ(info.dummy_cv_request->result.cert_status, kExpectedCertStatus);
dummy_cv()->ExpectReceivedNetlogSource(info.request_params,
info.net_log_source);
}
}
base::test::TaskEnvironment* task_environment() { return &task_environment_; }
mojo::Remote<mojom::CertVerifierService>& cv_service_remote() {
return cv_service_remote_;
}
mojo::Remote<mojom::CertVerifierServiceUpdater>& cv_service_updater_remote() {
return cv_service_updater_remote_;
}
DummyCertVerifier* dummy_cv() { return dummy_cv_; }
void set_dummy_cv(DummyCertVerifier* cv) { dummy_cv_ = cv; }
unsigned cv_service_client_changed_count() const {
return cv_service_client_changed_count_;
}
void OnCertVerifierChanged() override { cv_service_client_changed_count_++; }
private:
base::test::TaskEnvironment task_environment_;
mojo::Remote<mojom::CertVerifierService> cv_service_remote_;
mojo::Remote<mojom::CertVerifierServiceUpdater> cv_service_updater_remote_;
mojo::Receiver<mojom::CertVerifierServiceClient> cv_service_client_;
unsigned cv_service_client_changed_count_ = 0;
mojo::Remote<mojom::CertVerifierServiceFactory> cv_service_factory_remote_;
raw_ptr<DummyCertVerifier> dummy_cv_;
std::unique_ptr<CertVerifierServiceFactoryImpl> cv_service_factory_impl_;
};
}
TEST_F(CertVerifierServiceTest, TestSingleCompletion) {
CreateImpl(false);
TestCompletions(1, false);
}
TEST_F(CertVerifierServiceTest, TestMultipleSimultaneousCompletions) {
CreateImpl(false);
TestCompletions(5, false);
}
TEST_F(CertVerifierServiceTest, TestSingleSyncCompletion) {
CreateImpl(false);
TestCompletions(1, true);
}
TEST_F(CertVerifierServiceTest, TestMultipleSimultaneousSyncCompletions) {
CreateImpl(false);
TestCompletions(5, true);
}
TEST_F(CertVerifierServiceTest, TestInvalidIntermediate) {
CreateImpl(false);
auto leaf = GetTestCert();
std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
intermediates.push_back(
net::x509_util::CreateCryptoBuffer(std::string_view("F")));
scoped_refptr<net::X509Certificate> test_cert =
net::X509Certificate::CreateFromBuffer(bssl::UpRef(leaf->cert_buffer()),
std::move(intermediates));
ASSERT_TRUE(test_cert);
net::CertVerifier::RequestParams dummy_params(test_cert, "example.com", 0,
std::string(),
std::string());
DummyCVServiceRequest cv_service_req;
mojo::Receiver<mojom::CertVerifierRequest> cv_request_receiver(
&cv_service_req);
cv_service_remote()->Verify(
dummy_params,
net::NetLogSource(net::NetLogSourceType::CERT_VERIFIER_JOB,
1234, base::TimeTicks::Now()),
cv_request_receiver.BindNewPipeAndPassRemote());
cv_service_remote().FlushForTesting();
ASSERT_FALSE(cv_service_req.is_completed);
ASSERT_FALSE(cv_service_req.result.verified_cert);
dummy_cv()->RespondToRequest(dummy_params);
cv_service_remote().FlushForTesting();
ASSERT_TRUE(cv_service_req.is_completed);
EXPECT_EQ(cv_service_req.net_error, kExpectedNetError);
ASSERT_TRUE(cv_service_req.result.verified_cert);
EXPECT_TRUE(test_cert->EqualsIncludingChain(
cv_service_req.result.verified_cert.get()));
}
TEST_F(CertVerifierServiceTest, TestRequestDisconnectionCancelsCVRequest) {
CreateImpl(false);
net::CertVerifier::RequestParams dummy_params(GetTestCert(), "example.com", 0,
std::string(),
std::string());
DummyCVServiceRequest cv_service_req;
mojo::Receiver<mojom::CertVerifierRequest> cv_request_receiver(
&cv_service_req);
cv_service_remote()->Verify(
dummy_params,
net::NetLogSource(net::NetLogSourceType::CERT_VERIFIER_JOB, 1234,
base::TimeTicks::Now()),
cv_request_receiver.BindNewPipeAndPassRemote());
cv_service_remote().FlushForTesting();
ASSERT_FALSE(dummy_cv()->WasRequestCancelled(dummy_params));
cv_request_receiver.reset();
task_environment()->RunUntilIdle();
ASSERT_TRUE(dummy_cv()->WasRequestCancelled(dummy_params));
}
TEST_F(CertVerifierServiceTest, TestCVServiceDisconnection) {
CreateImpl(false);
net::CertVerifier::RequestParams dummy_params(GetTestCert(), "example.com", 0,
std::string(),
std::string());
bool disconnected = false;
DummyCVServiceRequest cv_service_req;
mojo::Receiver<mojom::CertVerifierRequest> cv_request_receiver(
&cv_service_req);
cv_service_remote()->Verify(
dummy_params,
net::NetLogSource(net::NetLogSourceType::CERT_VERIFIER_JOB, 1234,
base::TimeTicks::Now()),
cv_request_receiver.BindNewPipeAndPassRemote());
cv_request_receiver.set_disconnect_handler(
base::BindLambdaForTesting([&]() { disconnected = true; }));
set_dummy_cv(nullptr);
cv_service_remote().reset();
task_environment()->RunUntilIdle();
ASSERT_FALSE(cv_service_req.is_completed);
ASSERT_TRUE(disconnected);
}
TEST_F(CertVerifierServiceTest, StoresConfig) {
CreateImpl(false);
ASSERT_FALSE(dummy_cv()->config()->require_rev_checking_local_anchors);
net::CertVerifier::Config config;
config.require_rev_checking_local_anchors = true;
cv_service_remote()->SetConfig(config);
cv_service_remote().FlushForTesting();
ASSERT_TRUE(dummy_cv()->config()->require_rev_checking_local_anchors);
}
TEST_F(CertVerifierServiceTest, ObserverIsRegistered) {
CreateImpl(false);
ASSERT_TRUE(dummy_cv()->GetObserver());
EXPECT_EQ(cv_service_client_changed_count(), 0u);
dummy_cv()->GetObserver()->OnCertVerifierChanged();
task_environment()->RunUntilIdle();
EXPECT_EQ(cv_service_client_changed_count(), 1u);
dummy_cv()->GetObserver()->OnCertVerifierChanged();
task_environment()->RunUntilIdle();
EXPECT_EQ(cv_service_client_changed_count(), 2u);
}
TEST_F(CertVerifierServiceTest, TestSingleRequestQueued) {
CreateImpl(true);
net::CertVerifier::RequestParams dummy_params(GetTestCert(), "example.com", 0,
std::string(),
std::string());
DummyCVServiceRequest cv_service_req;
mojo::Receiver<mojom::CertVerifierRequest> cv_request_receiver(
&cv_service_req);
cv_service_remote()->Verify(
dummy_params,
net::NetLogSource(net::NetLogSourceType::CERT_VERIFIER_JOB, 1234,
base::TimeTicks::Now()),
cv_request_receiver.BindNewPipeAndPassRemote());
task_environment()->RunUntilIdle();
ASSERT_FALSE(dummy_cv()->HasPendingAsyncRequest(dummy_params));
ASSERT_FALSE(dummy_cv()->HasUpdatedProcData());
cv_service_updater_remote()->UpdateAdditionalCertificates(
cert_verifier::mojom::AdditionalCertificates::New());
cv_service_updater_remote().FlushForTesting();
ASSERT_TRUE(dummy_cv()->HasPendingAsyncRequest(dummy_params));
ASSERT_TRUE(dummy_cv()->HasUpdatedProcData());
}
TEST_F(CertVerifierServiceTest, TestSingleRequestQueuedWithDisconnection) {
CreateImpl(true);
net::CertVerifier::RequestParams dummy_params(GetTestCert(), "example.com", 0,
std::string(),
std::string());
DummyCVServiceRequest cv_service_req;
mojo::Receiver<mojom::CertVerifierRequest> cv_request_receiver(
&cv_service_req);
cv_service_remote()->Verify(
dummy_params,
net::NetLogSource(net::NetLogSourceType::CERT_VERIFIER_JOB, 1234,
base::TimeTicks::Now()),
cv_request_receiver.BindNewPipeAndPassRemote());
task_environment()->RunUntilIdle();
ASSERT_FALSE(dummy_cv()->HasPendingAsyncRequest(dummy_params));
ASSERT_FALSE(dummy_cv()->HasUpdatedProcData());
cv_request_receiver.reset();
task_environment()->RunUntilIdle();
cv_service_updater_remote()->UpdateAdditionalCertificates(
cert_verifier::mojom::AdditionalCertificates::New());
cv_service_updater_remote().FlushForTesting();
ASSERT_TRUE(dummy_cv()->HasPendingAsyncRequest(dummy_params));
ASSERT_TRUE(dummy_cv()->HasUpdatedProcData());
ASSERT_TRUE(dummy_cv()->WasRequestCancelled(dummy_params));
}
TEST_F(CertVerifierServiceTest, TestSingleQueuedRequestCVServiceDisconnection) {
CreateImpl(true);
net::CertVerifier::RequestParams dummy_params(GetTestCert(), "example.com", 0,
std::string(),
std::string());
DummyCVServiceRequest cv_service_req;
mojo::Receiver<mojom::CertVerifierRequest> cv_request_receiver(
&cv_service_req);
cv_service_remote()->Verify(
dummy_params,
net::NetLogSource(net::NetLogSourceType::CERT_VERIFIER_JOB, 1234,
base::TimeTicks::Now()),
cv_request_receiver.BindNewPipeAndPassRemote());
task_environment()->RunUntilIdle();
ASSERT_FALSE(dummy_cv()->HasPendingAsyncRequest(dummy_params));
ASSERT_FALSE(dummy_cv()->HasUpdatedProcData());
base::test::TestFuture<void> disconnected_future;
cv_request_receiver.set_disconnect_handler(base::BindLambdaForTesting(
[&]() { disconnected_future.GetCallback().Run(); }));
set_dummy_cv(nullptr);
cv_service_remote().reset();
ASSERT_TRUE(disconnected_future.Wait());
ASSERT_FALSE(cv_service_req.is_completed);
}
}