1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/ssl/client_cert_store_nss.h"
6
7 #include <cert.h>
8 #include <certt.h>
9 #include <pk11pub.h>
10
11 #include <memory>
12 #include <string>
13
14 #include "base/files/file_util.h"
15 #include "base/functional/bind.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/run_loop.h"
18 #include "base/test/scoped_feature_list.h"
19 #include "base/test/task_environment.h"
20 #include "crypto/nss_util.h"
21 #include "crypto/scoped_test_nss_db.h"
22 #include "net/base/features.h"
23 #include "net/cert/x509_certificate.h"
24 #include "net/cert/x509_util_nss.h"
25 #include "net/ssl/client_cert_identity_test_util.h"
26 #include "net/ssl/client_cert_store_unittest-inl.h"
27 #include "net/ssl/ssl_cert_request_info.h"
28 #include "net/ssl/ssl_private_key.h"
29 #include "net/ssl/ssl_private_key_test_util.h"
30 #include "net/test/cert_test_util.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32 #include "third_party/boringssl/src/include/openssl/ssl.h"
33 #include "third_party/boringssl/src/pki/pem.h"
34
35 namespace net {
36
37 namespace {
38
SaveIdentitiesAndQuitCallback(ClientCertIdentityList * out_identities,base::OnceClosure quit_closure,ClientCertIdentityList in_identities)39 void SaveIdentitiesAndQuitCallback(ClientCertIdentityList* out_identities,
40 base::OnceClosure quit_closure,
41 ClientCertIdentityList in_identities) {
42 *out_identities = std::move(in_identities);
43 std::move(quit_closure).Run();
44 }
45
SavePrivateKeyAndQuitCallback(scoped_refptr<net::SSLPrivateKey> * out_key,base::OnceClosure quit_closure,scoped_refptr<net::SSLPrivateKey> in_key)46 void SavePrivateKeyAndQuitCallback(scoped_refptr<net::SSLPrivateKey>* out_key,
47 base::OnceClosure quit_closure,
48 scoped_refptr<net::SSLPrivateKey> in_key) {
49 *out_key = std::move(in_key);
50 std::move(quit_closure).Run();
51 }
52
53 } // namespace
54
55 class ClientCertStoreNSSTestDelegate {
56 public:
ClientCertStoreNSSTestDelegate(bool use_new_impl)57 explicit ClientCertStoreNSSTestDelegate(bool use_new_impl) {
58 scoped_feature_list_.InitWithFeatureState(
59 net::features::kNewClientCertPathBuilding, use_new_impl);
60 }
61 ClientCertStoreNSSTestDelegate() = default;
62
SelectClientCerts(const CertificateList & input_certs,const SSLCertRequestInfo & cert_request_info,ClientCertIdentityList * selected_identities)63 bool SelectClientCerts(const CertificateList& input_certs,
64 const SSLCertRequestInfo& cert_request_info,
65 ClientCertIdentityList* selected_identities) {
66 *selected_identities =
67 FakeClientCertIdentityListFromCertificateList(input_certs);
68
69 // Filters |selected_identities| using the logic being used to filter the
70 // system store when GetClientCerts() is called.
71 crypto::EnsureNSSInit();
72 ClientCertStoreNSS::FilterCertsOnWorkerThread(selected_identities,
73 cert_request_info);
74 return true;
75 }
76
77 private:
78 base::test::ScopedFeatureList scoped_feature_list_;
79 };
80
81 class ClientCertStoreNSSTestDelegateOld
82 : public ClientCertStoreNSSTestDelegate {
83 public:
ClientCertStoreNSSTestDelegateOld()84 ClientCertStoreNSSTestDelegateOld() : ClientCertStoreNSSTestDelegate(false) {}
85 };
86
87 class ClientCertStoreNSSTestDelegateNew
88 : public ClientCertStoreNSSTestDelegate {
89 public:
ClientCertStoreNSSTestDelegateNew()90 ClientCertStoreNSSTestDelegateNew() : ClientCertStoreNSSTestDelegate(true) {}
91 };
92
93 INSTANTIATE_TYPED_TEST_SUITE_P(NSSOld,
94 ClientCertStoreTest,
95 ClientCertStoreNSSTestDelegateOld);
96
97 INSTANTIATE_TYPED_TEST_SUITE_P(NSSNew,
98 ClientCertStoreTest,
99 ClientCertStoreNSSTestDelegateNew);
100
101 class ClientCertStoreNSSTest : public testing::TestWithParam<bool> {
102 public:
ClientCertStoreNSSTest()103 ClientCertStoreNSSTest() {
104 scoped_feature_list_.InitWithFeatureState(
105 net::features::kNewClientCertPathBuilding, GetParam());
106 }
107
108 private:
109 base::test::ScopedFeatureList scoped_feature_list_;
110 };
111
112 // Tests that ClientCertStoreNSS attempts to build a certificate chain by
113 // querying NSS before return a certificate.
TEST_P(ClientCertStoreNSSTest,BuildsCertificateChain)114 TEST_P(ClientCertStoreNSSTest, BuildsCertificateChain) {
115 base::test::TaskEnvironment task_environment;
116
117 // Set up a test DB and import client_1.pem and client_1_ca.pem.
118 crypto::ScopedTestNSSDB test_db;
119 scoped_refptr<X509Certificate> client_1(ImportClientCertAndKeyFromFile(
120 GetTestCertsDirectory(), "client_1.pem", "client_1.pk8", test_db.slot()));
121 ASSERT_TRUE(client_1.get());
122 scoped_refptr<X509Certificate> client_1_ca(
123 ImportCertFromFile(GetTestCertsDirectory(), "client_1_ca.pem"));
124 ASSERT_TRUE(client_1_ca.get());
125 ASSERT_TRUE(ImportClientCertToSlot(client_1_ca, test_db.slot()));
126 std::string pkcs8_key;
127 ASSERT_TRUE(base::ReadFileToString(
128 GetTestCertsDirectory().AppendASCII("client_1.pk8"), &pkcs8_key));
129
130 auto store = std::make_unique<ClientCertStoreNSS>(
131 ClientCertStoreNSS::PasswordDelegateFactory());
132
133 // These test keys are RSA keys.
134 std::vector<uint16_t> expected = SSLPrivateKey::DefaultAlgorithmPreferences(
135 EVP_PKEY_RSA, true /* supports PSS */);
136
137 {
138 // Request certificates matching B CA, |client_1|'s issuer.
139 auto request = base::MakeRefCounted<SSLCertRequestInfo>();
140 request->cert_authorities.emplace_back(
141 reinterpret_cast<const char*>(kAuthority1DN), sizeof(kAuthority1DN));
142
143 ClientCertIdentityList selected_identities;
144 base::RunLoop loop;
145 store->GetClientCerts(
146 request, base::BindOnce(SaveIdentitiesAndQuitCallback,
147 &selected_identities, loop.QuitClosure()));
148 loop.Run();
149
150 // The result be |client_1| with no intermediates.
151 ASSERT_EQ(1u, selected_identities.size());
152 scoped_refptr<X509Certificate> selected_cert =
153 selected_identities[0]->certificate();
154 EXPECT_TRUE(x509_util::CryptoBufferEqual(client_1->cert_buffer(),
155 selected_cert->cert_buffer()));
156 ASSERT_EQ(0u, selected_cert->intermediate_buffers().size());
157
158 scoped_refptr<SSLPrivateKey> ssl_private_key;
159 base::RunLoop key_loop;
160 selected_identities[0]->AcquirePrivateKey(
161 base::BindOnce(SavePrivateKeyAndQuitCallback, &ssl_private_key,
162 key_loop.QuitClosure()));
163 key_loop.Run();
164
165 ASSERT_TRUE(ssl_private_key);
166 EXPECT_EQ(expected, ssl_private_key->GetAlgorithmPreferences());
167 TestSSLPrivateKeyMatches(ssl_private_key.get(), pkcs8_key);
168 }
169
170 {
171 // Request certificates matching C Root CA, |client_1_ca|'s issuer.
172 auto request = base::MakeRefCounted<SSLCertRequestInfo>();
173 request->cert_authorities.emplace_back(
174 reinterpret_cast<const char*>(kAuthorityRootDN),
175 sizeof(kAuthorityRootDN));
176
177 ClientCertIdentityList selected_identities;
178 base::RunLoop loop;
179 store->GetClientCerts(
180 request, base::BindOnce(SaveIdentitiesAndQuitCallback,
181 &selected_identities, loop.QuitClosure()));
182 loop.Run();
183
184 // The result be |client_1| with |client_1_ca| as an intermediate.
185 ASSERT_EQ(1u, selected_identities.size());
186 scoped_refptr<X509Certificate> selected_cert =
187 selected_identities[0]->certificate();
188 EXPECT_TRUE(x509_util::CryptoBufferEqual(client_1->cert_buffer(),
189 selected_cert->cert_buffer()));
190 ASSERT_EQ(1u, selected_cert->intermediate_buffers().size());
191 EXPECT_TRUE(x509_util::CryptoBufferEqual(
192 client_1_ca->cert_buffer(),
193 selected_cert->intermediate_buffers()[0].get()));
194
195 scoped_refptr<SSLPrivateKey> ssl_private_key;
196 base::RunLoop key_loop;
197 selected_identities[0]->AcquirePrivateKey(
198 base::BindOnce(SavePrivateKeyAndQuitCallback, &ssl_private_key,
199 key_loop.QuitClosure()));
200 key_loop.Run();
201 ASSERT_TRUE(ssl_private_key);
202 EXPECT_EQ(expected, ssl_private_key->GetAlgorithmPreferences());
203 TestSSLPrivateKeyMatches(ssl_private_key.get(), pkcs8_key);
204 }
205 }
206
TEST_P(ClientCertStoreNSSTest,SubjectPrintableStringContainingUTF8)207 TEST_P(ClientCertStoreNSSTest, SubjectPrintableStringContainingUTF8) {
208 base::test::TaskEnvironment task_environment;
209
210 crypto::ScopedTestNSSDB test_db;
211 base::FilePath certs_dir =
212 GetTestNetDataDirectory().AppendASCII("parse_certificate_unittest");
213
214 ASSERT_TRUE(ImportSensitiveKeyFromFile(
215 certs_dir, "v3_certificate_template.pk8", test_db.slot()));
216 std::string pkcs8_key;
217 ASSERT_TRUE(base::ReadFileToString(
218 certs_dir.AppendASCII("v3_certificate_template.pk8"), &pkcs8_key));
219
220 std::string file_data;
221 ASSERT_TRUE(base::ReadFileToString(
222 certs_dir.AppendASCII(
223 "subject_printable_string_containing_utf8_client_cert.pem"),
224 &file_data));
225
226 bssl::PEMTokenizer pem_tokenizer(file_data, {"CERTIFICATE"});
227 ASSERT_TRUE(pem_tokenizer.GetNext());
228 std::string cert_der(pem_tokenizer.data());
229 ASSERT_FALSE(pem_tokenizer.GetNext());
230
231 ScopedCERTCertificate cert(
232 x509_util::CreateCERTCertificateFromBytes(base::as_byte_span(cert_der)));
233 ASSERT_TRUE(cert);
234
235 ASSERT_TRUE(ImportClientCertToSlot(cert.get(), test_db.slot()));
236
237 auto store = std::make_unique<ClientCertStoreNSS>(
238 ClientCertStoreNSS::PasswordDelegateFactory());
239
240 // These test keys are RSA keys.
241 std::vector<uint16_t> expected = SSLPrivateKey::DefaultAlgorithmPreferences(
242 EVP_PKEY_RSA, true /* supports PSS */);
243
244 constexpr uint8_t kAuthorityDN[] = {
245 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
246 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,
247 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
248 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49,
249 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
250 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64};
251 auto request = base::MakeRefCounted<SSLCertRequestInfo>();
252 request->cert_authorities.emplace_back(
253 reinterpret_cast<const char*>(kAuthorityDN), sizeof(kAuthorityDN));
254
255 ClientCertIdentityList selected_identities;
256 base::RunLoop loop;
257 store->GetClientCerts(
258 request, base::BindOnce(SaveIdentitiesAndQuitCallback,
259 &selected_identities, loop.QuitClosure()));
260 loop.Run();
261
262 // The result be |cert| with no intermediates.
263 ASSERT_EQ(1u, selected_identities.size());
264 scoped_refptr<X509Certificate> selected_cert =
265 selected_identities[0]->certificate();
266 EXPECT_TRUE(x509_util::IsSameCertificate(cert.get(), selected_cert.get()));
267 EXPECT_EQ(0u, selected_cert->intermediate_buffers().size());
268
269 scoped_refptr<SSLPrivateKey> ssl_private_key;
270 base::RunLoop key_loop;
271 selected_identities[0]->AcquirePrivateKey(base::BindOnce(
272 SavePrivateKeyAndQuitCallback, &ssl_private_key, key_loop.QuitClosure()));
273 key_loop.Run();
274
275 ASSERT_TRUE(ssl_private_key);
276 EXPECT_EQ(expected, ssl_private_key->GetAlgorithmPreferences());
277 TestSSLPrivateKeyMatches(ssl_private_key.get(), pkcs8_key);
278 }
279
280 INSTANTIATE_TEST_SUITE_P(, ClientCertStoreNSSTest, testing::Bool());
281
282 // TODO(mattm): is it possible to unittest slot unlocking?
283
284 } // namespace net
285