1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chromeos/cert_loader.h"
8 #include "base/files/file_util.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/run_loop.h"
12 #include "crypto/scoped_nss_types.h"
13 #include "crypto/scoped_test_nss_db.h"
14 #include "net/base/test_data_directory.h"
15 #include "net/cert/nss_cert_database_chromeos.h"
16 #include "net/cert/x509_certificate.h"
17 #include "net/test/cert_test_util.h"
18 #include "testing/gtest/include/gtest/gtest.h"
23 bool IsCertInCertificateList(const net::X509Certificate
* cert
,
24 const net::CertificateList
& cert_list
) {
25 for (net::CertificateList::const_iterator it
= cert_list
.begin();
26 it
!= cert_list
.end();
28 if (net::X509Certificate::IsSameOSCert((*it
)->os_cert_handle(),
29 cert
->os_cert_handle())) {
36 class TestNSSCertDatabase
: public net::NSSCertDatabaseChromeOS
{
38 TestNSSCertDatabase(crypto::ScopedPK11Slot public_slot
,
39 crypto::ScopedPK11Slot private_slot
)
40 : NSSCertDatabaseChromeOS(public_slot
.Pass(), private_slot
.Pass()) {}
41 ~TestNSSCertDatabase() override
{}
43 void NotifyOfCertAdded(const net::X509Certificate
* cert
) {
44 NSSCertDatabaseChromeOS::NotifyObserversOfCertAdded(cert
);
48 class CertLoaderTest
: public testing::Test
,
49 public CertLoader::Observer
{
52 : cert_loader_(nullptr), certificates_loaded_events_count_(0U) {}
54 ~CertLoaderTest() override
{}
56 void SetUp() override
{
57 ASSERT_TRUE(primary_db_
.is_open());
59 CertLoader::Initialize();
60 cert_loader_
= CertLoader::Get();
61 cert_loader_
->AddObserver(this);
64 void TearDown() override
{
65 cert_loader_
->RemoveObserver(this);
66 CertLoader::Shutdown();
70 void StartCertLoaderWithPrimaryDB() {
71 CreateCertDatabase(&primary_db_
, &primary_certdb_
);
72 cert_loader_
->StartWithNSSDB(primary_certdb_
.get());
74 base::RunLoop().RunUntilIdle();
75 GetAndResetCertificatesLoadedEventsCount();
78 // CertLoader::Observer:
79 // The test keeps count of times the observer method was called.
80 void OnCertificatesLoaded(const net::CertificateList
& cert_list
,
81 bool initial_load
) override
{
82 EXPECT_TRUE(certificates_loaded_events_count_
== 0 || !initial_load
);
83 certificates_loaded_events_count_
++;
86 // Returns the number of |OnCertificatesLoaded| calls observed since the
87 // last call to this method equals |value|.
88 size_t GetAndResetCertificatesLoadedEventsCount() {
89 size_t result
= certificates_loaded_events_count_
;
90 certificates_loaded_events_count_
= 0;
94 void CreateCertDatabase(crypto::ScopedTestNSSDB
* db
,
95 scoped_ptr
<TestNSSCertDatabase
>* certdb
) {
96 ASSERT_TRUE(db
->is_open());
98 certdb
->reset(new TestNSSCertDatabase(
99 crypto::ScopedPK11Slot(PK11_ReferenceSlot(db
->slot())),
100 crypto::ScopedPK11Slot(PK11_ReferenceSlot(db
->slot()))));
101 (*certdb
)->SetSlowTaskRunnerForTest(message_loop_
.task_runner());
104 void ImportCACert(const std::string
& cert_file
,
105 net::NSSCertDatabase
* database
,
106 net::CertificateList
* imported_certs
) {
107 ASSERT_TRUE(database
);
108 ASSERT_TRUE(imported_certs
);
110 *imported_certs
= net::CreateCertificateListFromFile(
111 net::GetTestCertsDirectory(),
113 net::X509Certificate::FORMAT_AUTO
);
114 ASSERT_EQ(1U, imported_certs
->size());
116 net::NSSCertDatabase::ImportCertFailureList failed
;
117 ASSERT_TRUE(database
->ImportCACerts(*imported_certs
,
118 net::NSSCertDatabase::TRUST_DEFAULT
,
120 ASSERT_TRUE(failed
.empty());
123 scoped_refptr
<net::X509Certificate
> ImportClientCertAndKey(
124 TestNSSCertDatabase
* database
) {
125 // Import a client cert signed by that CA.
126 scoped_refptr
<net::X509Certificate
> client_cert(
127 net::ImportClientCertAndKeyFromFile(net::GetTestCertsDirectory(),
128 "client_1.pem", "client_1.pk8",
129 database
->GetPrivateSlot().get()));
130 database
->NotifyOfCertAdded(client_cert
.get());
134 CertLoader
* cert_loader_
;
136 // The user is primary as the one whose certificates CertLoader handles, it
137 // has nothing to do with crypto::InitializeNSSForChromeOSUser is_primary_user
138 // parameter (which is irrelevant for these tests).
139 crypto::ScopedTestNSSDB primary_db_
;
140 scoped_ptr
<TestNSSCertDatabase
> primary_certdb_
;
142 base::MessageLoop message_loop_
;
145 size_t certificates_loaded_events_count_
;
150 TEST_F(CertLoaderTest
, Basic
) {
151 EXPECT_FALSE(cert_loader_
->CertificatesLoading());
152 EXPECT_FALSE(cert_loader_
->certificates_loaded());
154 CreateCertDatabase(&primary_db_
, &primary_certdb_
);
155 cert_loader_
->StartWithNSSDB(primary_certdb_
.get());
157 EXPECT_FALSE(cert_loader_
->certificates_loaded());
158 EXPECT_TRUE(cert_loader_
->CertificatesLoading());
159 EXPECT_TRUE(cert_loader_
->cert_list().empty());
161 ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount());
162 base::RunLoop().RunUntilIdle();
163 EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount());
165 EXPECT_TRUE(cert_loader_
->certificates_loaded());
166 EXPECT_FALSE(cert_loader_
->CertificatesLoading());
168 // Default CA cert roots should get loaded.
169 EXPECT_FALSE(cert_loader_
->cert_list().empty());
172 TEST_F(CertLoaderTest
, CertLoaderUpdatesCertListOnNewCert
) {
173 StartCertLoaderWithPrimaryDB();
175 net::CertificateList certs
;
176 ImportCACert("root_ca_cert.pem", primary_certdb_
.get(), &certs
);
178 // Certs are loaded asynchronously, so the new cert should not yet be in the
181 IsCertInCertificateList(certs
[0].get(), cert_loader_
->cert_list()));
183 ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount());
184 base::RunLoop().RunUntilIdle();
185 EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount());
187 // The certificate list should be updated now, as the message loop's been run.
189 IsCertInCertificateList(certs
[0].get(), cert_loader_
->cert_list()));
191 EXPECT_FALSE(cert_loader_
->IsCertificateHardwareBacked(certs
[0].get()));
194 TEST_F(CertLoaderTest
, CertLoaderNoUpdateOnSecondaryDbChanges
) {
195 crypto::ScopedTestNSSDB secondary_db
;
196 scoped_ptr
<TestNSSCertDatabase
> secondary_certdb
;
198 StartCertLoaderWithPrimaryDB();
199 CreateCertDatabase(&secondary_db
, &secondary_certdb
);
201 net::CertificateList certs
;
202 ImportCACert("root_ca_cert.pem", secondary_certdb
.get(), &certs
);
204 base::RunLoop().RunUntilIdle();
207 IsCertInCertificateList(certs
[0].get(), cert_loader_
->cert_list()));
210 TEST_F(CertLoaderTest
, ClientLoaderUpdateOnNewClientCert
) {
211 StartCertLoaderWithPrimaryDB();
213 scoped_refptr
<net::X509Certificate
> cert(
214 ImportClientCertAndKey(primary_certdb_
.get()));
216 ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount());
217 base::RunLoop().RunUntilIdle();
218 EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount());
220 EXPECT_TRUE(IsCertInCertificateList(cert
.get(), cert_loader_
->cert_list()));
223 TEST_F(CertLoaderTest
, CertLoaderNoUpdateOnNewClientCertInSecondaryDb
) {
224 crypto::ScopedTestNSSDB secondary_db
;
225 scoped_ptr
<TestNSSCertDatabase
> secondary_certdb
;
227 StartCertLoaderWithPrimaryDB();
228 CreateCertDatabase(&secondary_db
, &secondary_certdb
);
230 scoped_refptr
<net::X509Certificate
> cert(
231 ImportClientCertAndKey(secondary_certdb
.get()));
233 base::RunLoop().RunUntilIdle();
235 EXPECT_FALSE(IsCertInCertificateList(cert
.get(), cert_loader_
->cert_list()));
238 TEST_F(CertLoaderTest
, UpdatedOnCertRemoval
) {
239 StartCertLoaderWithPrimaryDB();
241 scoped_refptr
<net::X509Certificate
> cert(
242 ImportClientCertAndKey(primary_certdb_
.get()));
244 base::RunLoop().RunUntilIdle();
246 ASSERT_EQ(1U, GetAndResetCertificatesLoadedEventsCount());
247 ASSERT_TRUE(IsCertInCertificateList(cert
.get(), cert_loader_
->cert_list()));
249 primary_certdb_
->DeleteCertAndKey(cert
.get());
251 ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount());
252 base::RunLoop().RunUntilIdle();
253 EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount());
255 ASSERT_FALSE(IsCertInCertificateList(cert
.get(), cert_loader_
->cert_list()));
258 TEST_F(CertLoaderTest
, UpdatedOnCACertTrustChange
) {
259 StartCertLoaderWithPrimaryDB();
261 net::CertificateList certs
;
262 ImportCACert("root_ca_cert.pem", primary_certdb_
.get(), &certs
);
264 base::RunLoop().RunUntilIdle();
265 ASSERT_EQ(1U, GetAndResetCertificatesLoadedEventsCount());
267 IsCertInCertificateList(certs
[0].get(), cert_loader_
->cert_list()));
269 // The value that should have been set by |ImportCACert|.
270 ASSERT_EQ(net::NSSCertDatabase::TRUST_DEFAULT
,
271 primary_certdb_
->GetCertTrust(certs
[0].get(), net::CA_CERT
));
272 ASSERT_TRUE(primary_certdb_
->SetCertTrust(certs
[0].get(), net::CA_CERT
,
273 net::NSSCertDatabase::TRUSTED_SSL
));
275 // Cert trust change should trigger certificate reload in cert_loader_.
276 ASSERT_EQ(0U, GetAndResetCertificatesLoadedEventsCount());
277 base::RunLoop().RunUntilIdle();
278 EXPECT_EQ(1U, GetAndResetCertificatesLoadedEventsCount());
281 } // namespace chromeos