#include "chrome/browser/certificate_manager_model.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/test_future.h"
#include "build/chromeos_buildflags.h"
#include "content/public/test/browser_task_environment.h"
#include "crypto/scoped_test_nss_db.h"
#include "net/cert/nss_cert_database.h"
#include "net/cert/scoped_nss_types.h"
#include "net/cert/x509_util_nss.h"
#include "net/ssl/client_cert_identity_test_util.h"
#include "net/test/cert_builder.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_data_directory.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/certificate_provider/certificate_provider.h"
#include "chromeos/ash/components/network/policy_certificate_provider.h"
#include "chromeos/components/onc/certificate_scope.h"
#include "chromeos/constants/chromeos_features.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chromeos/components/kcer/extra_instances.h"
#endif
namespace {
class FakeObserver : public CertificateManagerModel::Observer { … };
CertificateManagerModel::CertInfo* GetCertInfoFromOrgGroupingMap(
const CertificateManagerModel::OrgGroupingMap& org_grouping_map,
CERTCertificate* cert) { … }
}
class CertificateManagerModelTest : public testing::Test { … };
TEST_F(CertificateManagerModelTest, ListsCertsFromPlatform) { … }
TEST_F(CertificateManagerModelTest, ListsClientCertsFromPlatform) { … }
#if BUILDFLAG(IS_CHROMEOS)
namespace {
class FakePolicyCertificateProvider : public ash::PolicyCertificateProvider {
public:
void AddPolicyProvidedCertsObserver(Observer* observer) override {
observer_list_.AddObserver(observer);
}
void RemovePolicyProvidedCertsObserver(Observer* observer) override {
observer_list_.RemoveObserver(observer);
}
net::CertificateList GetAllServerAndAuthorityCertificates(
const chromeos::onc::CertificateScope& scope) const override {
EXPECT_EQ(chromeos::onc::CertificateScope::Default(), scope);
net::CertificateList merged;
merged.insert(merged.end(), web_trusted_certs_.begin(),
web_trusted_certs_.end());
merged.insert(merged.end(), not_web_trusted_certs_.begin(),
not_web_trusted_certs_.end());
return merged;
}
net::CertificateList GetAllAuthorityCertificates(
const chromeos::onc::CertificateScope& scope) const override {
NOTREACHED_IN_MIGRATION();
return net::CertificateList();
}
net::CertificateList GetWebTrustedCertificates(
const chromeos::onc::CertificateScope& scope) const override {
EXPECT_EQ(chromeos::onc::CertificateScope::Default(), scope);
return web_trusted_certs_;
}
net::CertificateList GetCertificatesWithoutWebTrust(
const chromeos::onc::CertificateScope& scope) const override {
EXPECT_EQ(chromeos::onc::CertificateScope::Default(), scope);
return not_web_trusted_certs_;
}
const std::set<std::string>& GetExtensionIdsWithPolicyCertificates()
const override {
NOTREACHED_IN_MIGRATION();
return kNoExtensions;
}
void SetPolicyProvidedCertificates(
const net::CertificateList& web_trusted_certs,
const net::CertificateList& not_web_trusted_certs) {
web_trusted_certs_ = web_trusted_certs;
not_web_trusted_certs_ = not_web_trusted_certs;
}
void NotifyObservers() {
for (auto& observer : observer_list_)
observer.OnPolicyProvidedCertsChanged();
}
private:
base::ObserverList<PolicyCertificateProvider::Observer,
true >::Unchecked observer_list_;
net::CertificateList web_trusted_certs_;
net::CertificateList not_web_trusted_certs_;
const std::set<std::string> kNoExtensions = {};
};
class FakeExtensionCertificateProvider : public chromeos::CertificateProvider {
public:
FakeExtensionCertificateProvider(
const net::CertificateList* extension_client_certificates,
const bool* extensions_hang)
: extension_client_certificates_(extension_client_certificates),
extensions_hang_(extensions_hang) {}
void GetCertificates(
base::OnceCallback<void(net::ClientCertIdentityList)> callback) override {
if (*extensions_hang_)
return;
std::move(callback).Run(FakeClientCertIdentityListFromCertificateList(
*extension_client_certificates_));
}
private:
raw_ptr<const net::CertificateList> extension_client_certificates_;
raw_ptr<const bool> extensions_hang_;
};
CertificateManagerModel::CertInfo* GetCertInfoFromOrgGroupingMap(
const CertificateManagerModel::OrgGroupingMap& org_grouping_map,
const net::X509Certificate* cert) {
for (const auto& org_and_cert_info_list : org_grouping_map) {
for (const auto& cert_info : org_and_cert_info_list.second) {
if (net::x509_util::IsSameCertificate(cert_info->cert(), cert))
return cert_info.get();
}
}
return nullptr;
}
}
class CertificateManagerModelChromeOSTest : public CertificateManagerModelTest {
protected:
std::unique_ptr<CertificateManagerModel::Params>
GetCertificateManagerModelParams() override {
auto params = std::make_unique<CertificateManagerModel::Params>();
params->policy_certs_provider = &policy_certs_provider_;
params->extension_certificate_provider =
std::make_unique<FakeExtensionCertificateProvider>(
&extension_client_certs_, &extensions_hang_);
#if BUILDFLAG(IS_CHROMEOS_ASH)
params->kcer = kcer::ExtraInstances::GetEmptyKcer();
#endif
return params;
}
void NotifyPolicyObserversAndWaitForRefresh() {
base::RunLoop run_loop;
fake_observer_->RunOnNextRefresh(run_loop.QuitClosure());
policy_certs_provider_.NotifyObservers();
run_loop.Run();
}
FakePolicyCertificateProvider policy_certs_provider_;
net::CertificateList extension_client_certs_;
bool extensions_hang_ = false;
};
TEST_F(CertificateManagerModelChromeOSTest, ListsWebTrustedCertsFromPolicy) {
scoped_refptr<net::X509Certificate> cert = net::ImportCertFromFile(
net::GetTestCertsDirectory(), "websocket_cacert.pem");
ASSERT_TRUE(cert.get());
policy_certs_provider_.SetPolicyProvidedCertificates({cert}, {});
NotifyPolicyObserversAndWaitForRefresh();
CertificateManagerModel::OrgGroupingMap org_grouping_map;
certificate_manager_model_->FilterAndBuildOrgGroupingMap(
net::CertType::CA_CERT, &org_grouping_map);
CertificateManagerModel::CertInfo* cert_info =
GetCertInfoFromOrgGroupingMap(org_grouping_map, cert.get());
ASSERT_TRUE(cert_info);
EXPECT_EQ(net::CertType::CA_CERT, cert_info->type());
EXPECT_EQ(u"pywebsocket", cert_info->name());
EXPECT_FALSE(cert_info->can_be_deleted());
EXPECT_FALSE(cert_info->untrusted());
EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPolicy,
cert_info->source());
EXPECT_TRUE(cert_info->web_trust_anchor());
EXPECT_FALSE(cert_info->hardware_backed());
}
TEST_F(CertificateManagerModelChromeOSTest, ListsNotWebTrustedCertsFromPolicy) {
scoped_refptr<net::X509Certificate> cert = net::ImportCertFromFile(
net::GetTestCertsDirectory(), "websocket_cacert.pem");
ASSERT_TRUE(cert.get());
policy_certs_provider_.SetPolicyProvidedCertificates({}, {cert});
NotifyPolicyObserversAndWaitForRefresh();
CertificateManagerModel::OrgGroupingMap org_grouping_map;
certificate_manager_model_->FilterAndBuildOrgGroupingMap(
net::CertType::CA_CERT, &org_grouping_map);
CertificateManagerModel::CertInfo* cert_info =
GetCertInfoFromOrgGroupingMap(org_grouping_map, cert.get());
ASSERT_TRUE(cert_info);
EXPECT_EQ(net::CertType::CA_CERT, cert_info->type());
EXPECT_EQ(u"pywebsocket", cert_info->name());
EXPECT_FALSE(cert_info->can_be_deleted());
EXPECT_FALSE(cert_info->untrusted());
EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPolicy,
cert_info->source());
EXPECT_FALSE(cert_info->web_trust_anchor());
EXPECT_FALSE(cert_info->hardware_backed());
}
TEST_F(CertificateManagerModelChromeOSTest,
WebTrustedPolicyCertsWinOverPlatformCerts) {
net::ScopedCERTCertificateList certs = CreateCERTCertificateListFromFile(
net::GetTestCertsDirectory(), "websocket_cacert.pem",
net::X509Certificate::FORMAT_AUTO);
ASSERT_EQ(1U, certs.size());
CERTCertificate* platform_cert = certs[0].get();
ASSERT_EQ(SECSuccess, PK11_ImportCert(test_nssdb_.slot(), platform_cert,
CK_INVALID_HANDLE, "cert",
PR_FALSE ));
scoped_refptr<net::X509Certificate> policy_cert = net::ImportCertFromFile(
net::GetTestCertsDirectory(), "websocket_cacert.pem");
ASSERT_TRUE(policy_cert.get());
policy_certs_provider_.SetPolicyProvidedCertificates({policy_cert}, {});
WaitForRefresh(true );
{
CertificateManagerModel::OrgGroupingMap org_grouping_map;
certificate_manager_model_->FilterAndBuildOrgGroupingMap(
net::CertType::CA_CERT, &org_grouping_map);
CertificateManagerModel::CertInfo* platform_cert_info =
GetCertInfoFromOrgGroupingMap(org_grouping_map, platform_cert);
ASSERT_TRUE(platform_cert_info);
CertificateManagerModel::CertInfo* policy_cert_info =
GetCertInfoFromOrgGroupingMap(org_grouping_map, policy_cert.get());
ASSERT_TRUE(policy_cert_info);
EXPECT_EQ(platform_cert_info, policy_cert_info);
EXPECT_EQ(net::CertType::CA_CERT, policy_cert_info->type());
EXPECT_EQ(u"pywebsocket", policy_cert_info->name());
EXPECT_FALSE(policy_cert_info->can_be_deleted());
EXPECT_FALSE(policy_cert_info->untrusted());
EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPolicy,
policy_cert_info->source());
EXPECT_TRUE(policy_cert_info->web_trust_anchor());
EXPECT_FALSE(policy_cert_info->hardware_backed());
}
policy_certs_provider_.SetPolicyProvidedCertificates({}, {});
NotifyPolicyObserversAndWaitForRefresh();
{
CertificateManagerModel::OrgGroupingMap org_grouping_map;
certificate_manager_model_->FilterAndBuildOrgGroupingMap(
net::CertType::CA_CERT, &org_grouping_map);
CertificateManagerModel::CertInfo* platform_cert_info =
GetCertInfoFromOrgGroupingMap(org_grouping_map, platform_cert);
ASSERT_TRUE(platform_cert_info);
EXPECT_EQ(net::CertType::CA_CERT, platform_cert_info->type());
EXPECT_EQ(u"pywebsocket", platform_cert_info->name());
EXPECT_TRUE(platform_cert_info->can_be_deleted());
EXPECT_TRUE(platform_cert_info->untrusted());
EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPlatform,
platform_cert_info->source());
EXPECT_FALSE(platform_cert_info->web_trust_anchor());
EXPECT_FALSE(platform_cert_info->hardware_backed());
}
}
TEST_F(CertificateManagerModelChromeOSTest,
PlatformCertsWinOverNotWebTrustedCerts) {
net::ScopedCERTCertificateList certs = CreateCERTCertificateListFromFile(
net::GetTestCertsDirectory(), "websocket_cacert.pem",
net::X509Certificate::FORMAT_AUTO);
ASSERT_EQ(1U, certs.size());
CERTCertificate* platform_cert = certs[0].get();
ASSERT_EQ(SECSuccess, PK11_ImportCert(test_nssdb_.slot(), platform_cert,
CK_INVALID_HANDLE, "cert",
PR_FALSE ));
scoped_refptr<net::X509Certificate> policy_cert = net::ImportCertFromFile(
net::GetTestCertsDirectory(), "websocket_cacert.pem");
ASSERT_TRUE(policy_cert.get());
policy_certs_provider_.SetPolicyProvidedCertificates({}, {policy_cert});
WaitForRefresh(true );
{
CertificateManagerModel::OrgGroupingMap org_grouping_map;
certificate_manager_model_->FilterAndBuildOrgGroupingMap(
net::CertType::CA_CERT, &org_grouping_map);
CertificateManagerModel::CertInfo* platform_cert_info =
GetCertInfoFromOrgGroupingMap(org_grouping_map, platform_cert);
ASSERT_TRUE(platform_cert_info);
CertificateManagerModel::CertInfo* policy_cert_info =
GetCertInfoFromOrgGroupingMap(org_grouping_map, policy_cert.get());
ASSERT_TRUE(policy_cert_info);
EXPECT_EQ(platform_cert_info, policy_cert_info);
EXPECT_EQ(net::CertType::CA_CERT, platform_cert_info->type());
EXPECT_EQ(u"pywebsocket", platform_cert_info->name());
EXPECT_TRUE(platform_cert_info->can_be_deleted());
EXPECT_TRUE(platform_cert_info->untrusted());
EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPlatform,
platform_cert_info->source());
EXPECT_FALSE(platform_cert_info->web_trust_anchor());
EXPECT_FALSE(platform_cert_info->hardware_backed());
}
base::RunLoop run_loop;
fake_observer_->RunOnNextRefresh(run_loop.QuitClosure());
base::test::TestFuture<bool> remove_result;
certificate_manager_model_->RemoveFromDatabase(
net::x509_util::DupCERTCertificate(platform_cert),
remove_result.GetCallback());
EXPECT_TRUE(remove_result.Get());
run_loop.Run();
{
CertificateManagerModel::OrgGroupingMap org_grouping_map;
certificate_manager_model_->FilterAndBuildOrgGroupingMap(
net::CertType::CA_CERT, &org_grouping_map);
CertificateManagerModel::CertInfo* policy_cert_info =
GetCertInfoFromOrgGroupingMap(org_grouping_map, policy_cert.get());
ASSERT_TRUE(policy_cert_info);
EXPECT_EQ(net::CertType::CA_CERT, policy_cert_info->type());
EXPECT_EQ(u"pywebsocket", policy_cert_info->name());
EXPECT_FALSE(policy_cert_info->can_be_deleted());
EXPECT_FALSE(policy_cert_info->untrusted());
EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPolicy,
policy_cert_info->source());
EXPECT_FALSE(policy_cert_info->web_trust_anchor());
EXPECT_FALSE(policy_cert_info->hardware_backed());
}
}
TEST_F(CertificateManagerModelChromeOSTest,
PlatformAndPolicyCertsListedWhenExtensionsHang) {
extensions_hang_ = true;
net::ScopedCERTCertificateList certs = CreateCERTCertificateListFromFile(
net::GetTestCertsDirectory(), "websocket_cacert.pem",
net::X509Certificate::FORMAT_AUTO);
ASSERT_EQ(1U, certs.size());
CERTCertificate* platform_cert = certs[0].get();
ASSERT_EQ(SECSuccess, PK11_ImportCert(test_nssdb_.slot(), platform_cert,
CK_INVALID_HANDLE, "cert",
PR_FALSE ));
scoped_refptr<net::X509Certificate> policy_cert =
net::ImportCertFromFile(net::GetTestCertsDirectory(), "root_ca_cert.pem");
ASSERT_TRUE(policy_cert.get());
policy_certs_provider_.SetPolicyProvidedCertificates({policy_cert}, {});
WaitForRefresh(true );
CertificateManagerModel::OrgGroupingMap org_grouping_map;
certificate_manager_model_->FilterAndBuildOrgGroupingMap(
net::CertType::CA_CERT, &org_grouping_map);
CertificateManagerModel::CertInfo* platform_cert_info =
GetCertInfoFromOrgGroupingMap(org_grouping_map, platform_cert);
ASSERT_TRUE(platform_cert_info);
CertificateManagerModel::CertInfo* policy_cert_info =
GetCertInfoFromOrgGroupingMap(org_grouping_map, policy_cert.get());
ASSERT_TRUE(policy_cert_info);
EXPECT_NE(platform_cert_info, policy_cert_info);
}
TEST_F(CertificateManagerModelChromeOSTest, ListsExtensionCerts) {
scoped_refptr<net::X509Certificate> extension_cert =
net::ImportCertFromFile(net::GetTestCertsDirectory(), "client_1.pem");
ASSERT_TRUE(extension_cert.get());
extension_client_certs_.push_back(extension_cert);
WaitForRefresh(true );
CertificateManagerModel::OrgGroupingMap org_grouping_map;
certificate_manager_model_->FilterAndBuildOrgGroupingMap(
net::CertType::USER_CERT, &org_grouping_map);
CertificateManagerModel::CertInfo* extension_cert_info =
GetCertInfoFromOrgGroupingMap(org_grouping_map, extension_cert.get());
ASSERT_TRUE(extension_cert_info);
EXPECT_EQ(net::CertType::USER_CERT, extension_cert_info->type());
EXPECT_EQ(u"Client Cert A (extension provided)", extension_cert_info->name());
EXPECT_FALSE(extension_cert_info->can_be_deleted());
EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kExtension,
extension_cert_info->source());
EXPECT_FALSE(extension_cert_info->web_trust_anchor());
EXPECT_FALSE(extension_cert_info->hardware_backed());
}
TEST_F(CertificateManagerModelChromeOSTest,
PlatformCertsWinOverExtensionCerts) {
net::ScopedCERTCertificate platform_client_cert;
net::ImportClientCertAndKeyFromFile(
net::GetTestCertsDirectory(), "client_1.pem", "client_1.pk8",
test_nssdb_.slot(), &platform_client_cert);
scoped_refptr<net::X509Certificate> extension_cert =
net::ImportCertFromFile(net::GetTestCertsDirectory(), "client_1.pem");
ASSERT_TRUE(extension_cert.get());
extension_client_certs_.push_back(extension_cert);
WaitForRefresh(true );
{
CertificateManagerModel::OrgGroupingMap org_grouping_map;
certificate_manager_model_->FilterAndBuildOrgGroupingMap(
net::CertType::USER_CERT, &org_grouping_map);
CertificateManagerModel::CertInfo* platform_cert_info =
GetCertInfoFromOrgGroupingMap(org_grouping_map,
platform_client_cert.get());
ASSERT_TRUE(platform_cert_info);
CertificateManagerModel::CertInfo* extension_cert_info =
GetCertInfoFromOrgGroupingMap(org_grouping_map, extension_cert.get());
ASSERT_TRUE(extension_cert_info);
EXPECT_EQ(platform_cert_info, extension_cert_info);
EXPECT_EQ(net::CertType::USER_CERT, platform_cert_info->type());
EXPECT_EQ(u"Client Cert A", platform_cert_info->name());
EXPECT_TRUE(platform_cert_info->can_be_deleted());
EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kPlatform,
platform_cert_info->source());
EXPECT_FALSE(platform_cert_info->web_trust_anchor());
EXPECT_FALSE(platform_cert_info->hardware_backed());
}
base::RunLoop run_loop;
fake_observer_->RunOnNextRefresh(run_loop.QuitClosure());
base::test::TestFuture<bool> remove_result;
certificate_manager_model_->RemoveFromDatabase(
std::move(platform_client_cert), remove_result.GetCallback());
EXPECT_TRUE(remove_result.Get());
run_loop.Run();
{
CertificateManagerModel::OrgGroupingMap org_grouping_map;
certificate_manager_model_->FilterAndBuildOrgGroupingMap(
net::CertType::USER_CERT, &org_grouping_map);
CertificateManagerModel::CertInfo* extension_cert_info =
GetCertInfoFromOrgGroupingMap(org_grouping_map, extension_cert.get());
ASSERT_TRUE(extension_cert_info);
EXPECT_EQ(net::CertType::USER_CERT, extension_cert_info->type());
EXPECT_EQ(u"Client Cert A (extension provided)",
extension_cert_info->name());
EXPECT_FALSE(extension_cert_info->can_be_deleted());
EXPECT_EQ(CertificateManagerModel::CertInfo::Source::kExtension,
extension_cert_info->source());
EXPECT_FALSE(extension_cert_info->web_trust_anchor());
EXPECT_FALSE(extension_cert_info->hardware_backed());
}
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(CertificateManagerModelChromeOSTest, ImportFromPKCS12) {
std::string kInvalidPkcs12Data = "111";
std::u16string kPassword = u"222";
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(
chromeos::features::kEnablePkcs12ToChapsDualWrite);
{
base::test::TestFuture<int> import_waiter;
certificate_manager_model_->ImportFromPKCS12(
test_nssdb_.slot(), kInvalidPkcs12Data, kPassword,
false, import_waiter.GetCallback());
EXPECT_EQ(import_waiter.Get(), net::ERR_PKCS12_IMPORT_INVALID_FILE);
}
{
base::test::TestFuture<int> import_waiter;
certificate_manager_model_->ImportFromPKCS12(
test_nssdb_.slot(), kInvalidPkcs12Data, kPassword,
true, import_waiter.GetCallback());
EXPECT_EQ(import_waiter.Get(), net::ERR_PKCS12_IMPORT_INVALID_FILE);
}
feature_list.Reset();
feature_list.InitAndEnableFeature(
chromeos::features::kEnablePkcs12ToChapsDualWrite);
{
base::test::TestFuture<int> import_waiter;
certificate_manager_model_->ImportFromPKCS12(
test_nssdb_.slot(), kInvalidPkcs12Data, kPassword,
false, import_waiter.GetCallback());
EXPECT_EQ(import_waiter.Get(), net::ERR_PKCS12_IMPORT_INVALID_FILE);
}
{
base::test::TestFuture<int> import_waiter;
certificate_manager_model_->ImportFromPKCS12(
test_nssdb_.slot(), kInvalidPkcs12Data, kPassword,
true, import_waiter.GetCallback());
EXPECT_EQ(import_waiter.Get(), net::ERR_PKCS12_IMPORT_INVALID_FILE);
}
}
#endif
#endif