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