• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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