1 // Copyright 2013 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 "net/ssl/client_cert_store_chromeos.h"
10 #include "base/callback.h"
11 #include "base/file_util.h"
12 #include "base/run_loop.h"
13 #include "crypto/nss_util_internal.h"
14 #include "crypto/rsa_private_key.h"
15 #include "crypto/scoped_test_nss_chromeos_user.h"
16 #include "crypto/scoped_test_system_nss_key_slot.h"
17 #include "net/base/test_data_directory.h"
18 #include "net/cert/x509_certificate.h"
19 #include "net/ssl/client_cert_store_unittest-inl.h"
20 #include "net/test/cert_test_util.h"
31 enum SystemSlotAvailability
{
32 SYSTEM_SLOT_AVAILABILITY_ENABLED
,
33 SYSTEM_SLOT_AVAILABILITY_DISABLED
38 // Define a delegate to be used for instantiating the parameterized test set
39 // ClientCertStoreTest.
40 template <ReadFromSlot read_from
,
41 SystemSlotAvailability system_slot_availability
>
42 class ClientCertStoreChromeOSTestDelegate
{
44 ClientCertStoreChromeOSTestDelegate()
45 : user_("scopeduser"),
46 store_(system_slot_availability
== SYSTEM_SLOT_AVAILABILITY_ENABLED
,
47 user_
.username_hash(),
48 ClientCertStoreChromeOS::PasswordDelegateFactory()) {
49 // Defer futher initialization and checks to SelectClientCerts, because the
50 // constructor doesn't allow us to return an initialization result. Could be
51 // cleaned up by adding an Init() function.
54 // Called by the ClientCertStoreTest tests.
55 // |inpurt_certs| contains certificates to select from. Because
56 // ClientCertStoreChromeOS filters also for the right slot, we have to import
57 // the certs at first.
58 // Since the certs are imported, the store can be tested by using its public
59 // interface (GetClientCerts), which will read the certs from NSS.
60 bool SelectClientCerts(const CertificateList
& input_certs
,
61 const SSLCertRequestInfo
& cert_request_info
,
62 CertificateList
* selected_certs
) {
63 if (!user_
.constructed_successfully()) {
64 LOG(ERROR
) << "Scoped test user DB could not be constructed.";
69 crypto::ScopedPK11Slot slot
;
71 case READ_FROM_SLOT_USER
:
72 slot
= crypto::GetPublicSlotForChromeOSUser(user_
.username_hash());
74 case READ_FROM_SLOT_SYSTEM
:
75 slot
.reset(PK11_ReferenceSlot(system_db_
.slot()));
81 LOG(ERROR
) << "Could not get the NSS key slot";
85 // Only user certs are considered for the cert request, which means that the
86 // private key must be known to NSS. Import all private keys for certs that
87 // are used througout the test.
88 if (!ImportSensitiveKeyFromFile(
89 GetTestCertsDirectory(), "client_1.pk8", slot
.get()) ||
90 !ImportSensitiveKeyFromFile(
91 GetTestCertsDirectory(), "client_2.pk8", slot
.get())) {
95 for (CertificateList::const_iterator it
= input_certs
.begin();
96 it
!= input_certs
.end();
98 if (!ImportClientCertToSlot(*it
, slot
.get()))
101 base::RunLoop run_loop
;
102 store_
.GetClientCerts(
103 cert_request_info
, selected_certs
, run_loop
.QuitClosure());
109 crypto::ScopedTestNSSChromeOSUser user_
;
110 crypto::ScopedTestSystemNSSKeySlot system_db_
;
111 ClientCertStoreChromeOS store_
;
114 // ClientCertStoreChromeOS derives from ClientCertStoreNSS and delegates the
115 // filtering by issuer to that base class.
116 // To verify that this delegation is functional, run the same filtering tests as
117 // for the other implementations. These tests are defined in
118 // client_cert_store_unittest-inl.h and are instantiated for each platform.
120 // In this case, all requested certs are read from the user's slot and the
121 // system slot is not enabled in the store.
122 typedef ClientCertStoreChromeOSTestDelegate
<READ_FROM_SLOT_USER
,
123 SYSTEM_SLOT_AVAILABILITY_DISABLED
>
124 DelegateReadUserDisableSystem
;
125 INSTANTIATE_TYPED_TEST_CASE_P(ChromeOS_ReadUserDisableSystem
,
127 DelegateReadUserDisableSystem
);
129 // In this case, all requested certs are read from the user's slot and the
130 // system slot is enabled in the store.
131 typedef ClientCertStoreChromeOSTestDelegate
<READ_FROM_SLOT_USER
,
132 SYSTEM_SLOT_AVAILABILITY_ENABLED
>
133 DelegateReadUserEnableSystem
;
134 INSTANTIATE_TYPED_TEST_CASE_P(ChromeOS_ReadUserEnableSystem
,
136 DelegateReadUserEnableSystem
);
138 // In this case, all requested certs are read from the system slot, therefore
139 // the system slot is enabled in the store.
140 typedef ClientCertStoreChromeOSTestDelegate
<READ_FROM_SLOT_SYSTEM
,
141 SYSTEM_SLOT_AVAILABILITY_ENABLED
>
143 INSTANTIATE_TYPED_TEST_CASE_P(ChromeOS_ReadSystem
,
147 class ClientCertStoreChromeOSTest
: public ::testing::Test
{
149 scoped_refptr
<X509Certificate
> ImportCertForUser(
150 const std::string
& username_hash
,
151 const std::string
& cert_filename
,
152 const std::string
& key_filename
) {
153 crypto::ScopedPK11Slot
slot(
154 crypto::GetPublicSlotForChromeOSUser(username_hash
));
156 LOG(ERROR
) << "No slot for user " << username_hash
;
160 return ImportClientCertAndKeyFromFile(
161 GetTestCertsDirectory(), cert_filename
, key_filename
, slot
.get());
166 // Ensure that cert requests, that are started before the user's NSS DB is
167 // initialized, will wait for the initialization and succeed afterwards.
168 TEST_F(ClientCertStoreChromeOSTest
, RequestWaitsForNSSInitAndSucceeds
) {
169 crypto::ScopedTestNSSChromeOSUser
user("scopeduser");
170 ASSERT_TRUE(user
.constructed_successfully());
172 crypto::ScopedTestSystemNSSKeySlot system_slot
;
174 ClientCertStoreChromeOS
store(
175 true /* use system slot */,
176 user
.username_hash(),
177 ClientCertStoreChromeOS::PasswordDelegateFactory());
178 scoped_refptr
<X509Certificate
> cert_1(
179 ImportCertForUser(user
.username_hash(), "client_1.pem", "client_1.pk8"));
182 // Request any client certificate, which is expected to match client_1.
183 scoped_refptr
<SSLCertRequestInfo
> request_all(new SSLCertRequestInfo());
185 base::RunLoop run_loop
;
186 store
.GetClientCerts(
187 *request_all
, &request_all
->client_certs
, run_loop
.QuitClosure());
190 base::RunLoop run_loop_inner
;
191 run_loop_inner
.RunUntilIdle();
192 // GetClientCerts should wait for the initialization of the user's DB to
194 ASSERT_EQ(0u, request_all
->client_certs
.size());
196 // This should trigger the GetClientCerts operation to finish and to call
202 ASSERT_EQ(1u, request_all
->client_certs
.size());
205 // Ensure that cert requests, that are started after the user's NSS DB was
206 // initialized, will succeed.
207 TEST_F(ClientCertStoreChromeOSTest
, RequestsAfterNSSInitSucceed
) {
208 crypto::ScopedTestNSSChromeOSUser
user("scopeduser");
209 ASSERT_TRUE(user
.constructed_successfully());
212 crypto::ScopedTestSystemNSSKeySlot system_slot
;
214 ClientCertStoreChromeOS
store(
215 true /* use system slot */,
216 user
.username_hash(),
217 ClientCertStoreChromeOS::PasswordDelegateFactory());
218 scoped_refptr
<X509Certificate
> cert_1(
219 ImportCertForUser(user
.username_hash(), "client_1.pem", "client_1.pk8"));
222 scoped_refptr
<SSLCertRequestInfo
> request_all(new SSLCertRequestInfo());
224 base::RunLoop run_loop
;
225 store
.GetClientCerts(
226 *request_all
, &request_all
->client_certs
, run_loop
.QuitClosure());
229 ASSERT_EQ(1u, request_all
->client_certs
.size());
232 // This verifies that a request in the context of User1 doesn't see certificates
233 // of User2, and the other way round. We check both directions, to ensure that
234 // the behavior doesn't depend on initialization order of the DBs, for example.
235 TEST_F(ClientCertStoreChromeOSTest
, RequestDoesCrossReadOtherUserDB
) {
236 crypto::ScopedTestNSSChromeOSUser
user1("scopeduser1");
237 ASSERT_TRUE(user1
.constructed_successfully());
238 crypto::ScopedTestNSSChromeOSUser
user2("scopeduser2");
239 ASSERT_TRUE(user2
.constructed_successfully());
244 crypto::ScopedTestSystemNSSKeySlot system_slot
;
246 ClientCertStoreChromeOS
store1(
247 true /* use system slot */,
248 user1
.username_hash(),
249 ClientCertStoreChromeOS::PasswordDelegateFactory());
250 ClientCertStoreChromeOS
store2(
251 true /* use system slot */,
252 user2
.username_hash(),
253 ClientCertStoreChromeOS::PasswordDelegateFactory());
255 scoped_refptr
<X509Certificate
> cert_1(
256 ImportCertForUser(user1
.username_hash(), "client_1.pem", "client_1.pk8"));
258 scoped_refptr
<X509Certificate
> cert_2(
259 ImportCertForUser(user2
.username_hash(), "client_2.pem", "client_2.pk8"));
262 scoped_refptr
<SSLCertRequestInfo
> request_all(new SSLCertRequestInfo());
264 base::RunLoop run_loop_1
;
265 base::RunLoop run_loop_2
;
267 CertificateList selected_certs1
, selected_certs2
;
268 store1
.GetClientCerts(
269 *request_all
, &selected_certs1
, run_loop_1
.QuitClosure());
270 store2
.GetClientCerts(
271 *request_all
, &selected_certs2
, run_loop_2
.QuitClosure());
276 // store1 should only return certs of user1, namely cert_1.
277 ASSERT_EQ(1u, selected_certs1
.size());
278 EXPECT_TRUE(cert_1
->Equals(selected_certs1
[0]));
280 // store2 should only return certs of user2, namely cert_2.
281 ASSERT_EQ(1u, selected_certs2
.size());
282 EXPECT_TRUE(cert_2
->Equals(selected_certs2
[0]));
285 // This verifies that a request in the context of User1 doesn't see certificates
286 // of the system store if the system store is disabled.
287 TEST_F(ClientCertStoreChromeOSTest
, RequestDoesCrossReadSystemDB
) {
288 crypto::ScopedTestNSSChromeOSUser
user1("scopeduser1");
289 ASSERT_TRUE(user1
.constructed_successfully());
293 crypto::ScopedTestSystemNSSKeySlot system_slot
;
295 ClientCertStoreChromeOS
store(
296 false /* do not use system slot */,
297 user1
.username_hash(),
298 ClientCertStoreChromeOS::PasswordDelegateFactory());
300 scoped_refptr
<X509Certificate
> cert_1(
301 ImportCertForUser(user1
.username_hash(), "client_1.pem", "client_1.pk8"));
303 scoped_refptr
<X509Certificate
> cert_2(
304 ImportClientCertAndKeyFromFile(GetTestCertsDirectory(),
307 system_slot
.slot()));
310 scoped_refptr
<SSLCertRequestInfo
> request_all(new SSLCertRequestInfo());
312 base::RunLoop run_loop
;
314 CertificateList selected_certs
;
315 store
.GetClientCerts(*request_all
, &selected_certs
, run_loop
.QuitClosure());
319 // store should only return certs of the user, namely cert_1.
320 ASSERT_EQ(1u, selected_certs
.size());
321 EXPECT_TRUE(cert_1
->Equals(selected_certs
[0]));