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"
9 #include "base/callback.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/run_loop.h"
12 #include "crypto/rsa_private_key.h"
13 #include "crypto/scoped_test_nss_db.h"
14 #include "net/base/test_data_directory.h"
15 #include "net/cert/x509_certificate.h"
16 #include "net/ssl/client_cert_store_unittest-inl.h"
17 #include "net/test/cert_test_util.h"
23 class TestCertFilter
: public net::ClientCertStoreChromeOS::CertFilter
{
25 explicit TestCertFilter(bool init_finished
)
26 : init_finished_(init_finished
), init_called_(false) {}
28 ~TestCertFilter() override
{}
30 bool Init(const base::Closure
& callback
) override
{
34 pending_callback_
= callback
;
39 const scoped_refptr
<net::X509Certificate
>& cert
) const override
{
40 if (not_allowed_cert_
.get() && cert
->Equals(not_allowed_cert_
.get()))
45 bool init_called() { return init_called_
; }
48 init_finished_
= true;
49 base::MessageLoop::current()->PostTask(FROM_HERE
, pending_callback_
);
50 pending_callback_
.Reset();
53 void SetNotAllowedCert(scoped_refptr
<X509Certificate
> cert
) {
54 not_allowed_cert_
= cert
;
60 base::Closure pending_callback_
;
61 scoped_refptr
<X509Certificate
> not_allowed_cert_
;
66 // Define a delegate to be used for instantiating the parameterized test set
67 // ClientCertStoreTest.
68 class ClientCertStoreChromeOSTestDelegate
{
70 ClientCertStoreChromeOSTestDelegate()
72 make_scoped_ptr(new TestCertFilter(true /* init synchronously */)),
73 ClientCertStoreChromeOS::PasswordDelegateFactory()) {
74 // Defer futher initialization and checks to SelectClientCerts, because the
75 // constructor doesn't allow us to return an initialization result. Could be
76 // cleaned up by adding an Init() function.
79 // Called by the ClientCertStoreTest tests.
80 // |inpurt_certs| contains certificates to select from. Because
81 // ClientCertStoreChromeOS filters also for the right slot, we have to import
82 // the certs at first.
83 // Since the certs are imported, the store can be tested by using its public
84 // interface (GetClientCerts), which will read the certs from NSS.
85 bool SelectClientCerts(const CertificateList
& input_certs
,
86 const SSLCertRequestInfo
& cert_request_info
,
87 CertificateList
* selected_certs
) {
88 if (!test_db_
.is_open()) {
89 LOG(ERROR
) << "NSS DB could not be constructed.";
93 // Only user certs are considered for the cert request, which means that the
94 // private key must be known to NSS. Import all private keys for certs that
95 // are used througout the test.
96 if (!ImportSensitiveKeyFromFile(
97 GetTestCertsDirectory(), "client_1.pk8", test_db_
.slot()) ||
98 !ImportSensitiveKeyFromFile(
99 GetTestCertsDirectory(), "client_2.pk8", test_db_
.slot())) {
103 for (CertificateList::const_iterator it
= input_certs
.begin();
104 it
!= input_certs
.end();
106 if (!ImportClientCertToSlot(*it
, test_db_
.slot()))
109 base::RunLoop run_loop
;
110 store_
.GetClientCerts(
111 cert_request_info
, selected_certs
, run_loop
.QuitClosure());
117 crypto::ScopedTestNSSDB test_db_
;
118 ClientCertStoreChromeOS store_
;
121 // ClientCertStoreChromeOS derives from ClientCertStoreNSS and delegates the
122 // filtering by issuer to that base class.
123 // To verify that this delegation is functional, run the same filtering tests as
124 // for the other implementations. These tests are defined in
125 // client_cert_store_unittest-inl.h and are instantiated for each platform.
126 INSTANTIATE_TYPED_TEST_CASE_P(ClientCertStoreTestChromeOS
,
128 ClientCertStoreChromeOSTestDelegate
);
130 class ClientCertStoreChromeOSTest
: public ::testing::Test
{
132 scoped_refptr
<X509Certificate
> ImportCertToSlot(
133 const std::string
& cert_filename
,
134 const std::string
& key_filename
,
135 PK11SlotInfo
* slot
) {
136 return ImportClientCertAndKeyFromFile(
137 GetTestCertsDirectory(), cert_filename
, key_filename
, slot
);
141 // Ensure that cert requests, that are started before the filter is initialized,
142 // will wait for the initialization and succeed afterwards.
143 TEST_F(ClientCertStoreChromeOSTest
, RequestWaitsForNSSInitAndSucceeds
) {
144 crypto::ScopedTestNSSDB test_db
;
145 ASSERT_TRUE(test_db
.is_open());
147 TestCertFilter
* cert_filter
=
148 new TestCertFilter(false /* init asynchronously */);
149 ClientCertStoreChromeOS
store(
150 make_scoped_ptr(cert_filter
),
151 ClientCertStoreChromeOS::PasswordDelegateFactory());
153 scoped_refptr
<X509Certificate
> cert_1(
154 ImportCertToSlot("client_1.pem", "client_1.pk8", test_db
.slot()));
155 ASSERT_TRUE(cert_1
.get());
157 // Request any client certificate, which is expected to match client_1.
158 scoped_refptr
<SSLCertRequestInfo
> request_all(new SSLCertRequestInfo());
160 base::RunLoop run_loop
;
161 store
.GetClientCerts(
162 *request_all
, &request_all
->client_certs
, run_loop
.QuitClosure());
165 base::RunLoop run_loop_inner
;
166 run_loop_inner
.RunUntilIdle();
167 // GetClientCerts should wait for the initialization of the filter to
169 ASSERT_EQ(0u, request_all
->client_certs
.size());
170 EXPECT_TRUE(cert_filter
->init_called());
172 cert_filter
->FinishInit();
175 ASSERT_EQ(1u, request_all
->client_certs
.size());
178 // Ensure that cert requests, that are started after the filter was initialized,
180 TEST_F(ClientCertStoreChromeOSTest
, RequestsAfterNSSInitSucceed
) {
181 crypto::ScopedTestNSSDB test_db
;
182 ASSERT_TRUE(test_db
.is_open());
184 ClientCertStoreChromeOS
store(
185 make_scoped_ptr(new TestCertFilter(true /* init synchronously */)),
186 ClientCertStoreChromeOS::PasswordDelegateFactory());
188 scoped_refptr
<X509Certificate
> cert_1(
189 ImportCertToSlot("client_1.pem", "client_1.pk8", test_db
.slot()));
190 ASSERT_TRUE(cert_1
.get());
192 scoped_refptr
<SSLCertRequestInfo
> request_all(new SSLCertRequestInfo());
194 base::RunLoop run_loop
;
195 store
.GetClientCerts(
196 *request_all
, &request_all
->client_certs
, run_loop
.QuitClosure());
199 ASSERT_EQ(1u, request_all
->client_certs
.size());
202 TEST_F(ClientCertStoreChromeOSTest
, Filter
) {
203 crypto::ScopedTestNSSDB test_db
;
204 ASSERT_TRUE(test_db
.is_open());
206 TestCertFilter
* cert_filter
=
207 new TestCertFilter(true /* init synchronously */);
208 ClientCertStoreChromeOS
store(
209 make_scoped_ptr(cert_filter
),
210 ClientCertStoreChromeOS::PasswordDelegateFactory());
212 scoped_refptr
<X509Certificate
> cert_1(
213 ImportCertToSlot("client_1.pem", "client_1.pk8", test_db
.slot()));
214 ASSERT_TRUE(cert_1
.get());
215 scoped_refptr
<X509Certificate
> cert_2(
216 ImportCertToSlot("client_2.pem", "client_2.pk8", test_db
.slot()));
217 ASSERT_TRUE(cert_2
.get());
219 scoped_refptr
<SSLCertRequestInfo
> request_all(new SSLCertRequestInfo());
222 base::RunLoop run_loop
;
223 cert_filter
->SetNotAllowedCert(cert_2
);
224 CertificateList selected_certs
;
225 store
.GetClientCerts(*request_all
, &selected_certs
, run_loop
.QuitClosure());
228 ASSERT_EQ(1u, selected_certs
.size());
229 EXPECT_TRUE(cert_1
->Equals(selected_certs
[0].get()));
233 base::RunLoop run_loop
;
234 cert_filter
->SetNotAllowedCert(cert_1
);
235 CertificateList selected_certs
;
236 store
.GetClientCerts(*request_all
, &selected_certs
, run_loop
.QuitClosure());
239 ASSERT_EQ(1u, selected_certs
.size());
240 EXPECT_TRUE(cert_2
->Equals(selected_certs
[0].get()));