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_mac.h"
6
7 #include <memory>
8
9 #include "base/strings/string_number_conversions.h"
10 #include "net/cert/x509_util_apple.h"
11 #include "net/ssl/client_cert_identity_mac.h"
12 #include "net/ssl/client_cert_identity_test_util.h"
13 #include "net/ssl/client_cert_store_unittest-inl.h"
14 #include "net/ssl/ssl_private_key.h"
15 #include "net/test/cert_builder.h"
16 #include "third_party/boringssl/src/pki/extended_key_usage.h"
17 #include "third_party/boringssl/src/pki/parse_certificate.h"
18
19 namespace net {
20
21 namespace {
22
23 std::vector<std::unique_ptr<ClientCertIdentityMac>>
ClientCertIdentityMacListFromCertificateList(const CertificateList & certs)24 ClientCertIdentityMacListFromCertificateList(const CertificateList& certs) {
25 // This doesn't quite construct a real `ClientCertIdentityMac` because the
26 // `SecIdentityRef` is null. This means `SelectClientCertsForTesting` must
27 // turn off the KeyChain query. If this becomes an issue, change
28 // client_cert_store_unittest-inl.h to pass in the key data.
29 //
30 // Actually constructing a `SecIdentityRef` without persisting it is not
31 // currently possible with macOS's non-deprecated APIs, but it is possible
32 // with deprecated APIs using `SecKeychainCreate` and `SecItemImport`. See git
33 // history for net/test/keychain_test_util_mac.cc.
34 std::vector<std::unique_ptr<ClientCertIdentityMac>> identities;
35 identities.reserve(certs.size());
36 for (const auto& cert : certs) {
37 identities.push_back(std::make_unique<ClientCertIdentityMac>(
38 cert, base::apple::ScopedCFTypeRef<SecIdentityRef>()));
39 }
40 return identities;
41 }
42
InputVectorToString(std::vector<bssl::der::Input> vec)43 std::string InputVectorToString(std::vector<bssl::der::Input> vec) {
44 std::string r = "{";
45 std::string sep;
46 for (const auto& element : vec) {
47 r += sep;
48 r += base::HexEncode(element);
49 sep = ',';
50 }
51 r += '}';
52 return r;
53 }
54
KeyUsageVectorToString(std::vector<bssl::KeyUsageBit> vec)55 std::string KeyUsageVectorToString(std::vector<bssl::KeyUsageBit> vec) {
56 std::string r = "{";
57 std::string sep;
58 for (const auto& element : vec) {
59 r += sep;
60 r += base::NumberToString(static_cast<int>(element));
61 sep = ',';
62 }
63 r += '}';
64 return r;
65 }
66
67 } // namespace
68
69 class ClientCertStoreMacTestDelegate {
70 public:
SelectClientCerts(const CertificateList & input_certs,const SSLCertRequestInfo & cert_request_info,ClientCertIdentityList * selected_certs)71 bool SelectClientCerts(const CertificateList& input_certs,
72 const SSLCertRequestInfo& cert_request_info,
73 ClientCertIdentityList* selected_certs) {
74 return store_.SelectClientCertsForTesting(
75 ClientCertIdentityMacListFromCertificateList(input_certs),
76 cert_request_info, selected_certs);
77 }
78
79 private:
80 ClientCertStoreMac store_;
81 };
82
83 INSTANTIATE_TYPED_TEST_SUITE_P(Mac,
84 ClientCertStoreTest,
85 ClientCertStoreMacTestDelegate);
86
87 class ClientCertStoreMacTest : public ::testing::Test {
88 protected:
SelectClientCerts(const CertificateList & input_certs,const SSLCertRequestInfo & cert_request_info,ClientCertIdentityList * selected_certs)89 bool SelectClientCerts(const CertificateList& input_certs,
90 const SSLCertRequestInfo& cert_request_info,
91 ClientCertIdentityList* selected_certs) {
92 return store_.SelectClientCertsForTesting(
93 ClientCertIdentityMacListFromCertificateList(input_certs),
94 cert_request_info, selected_certs);
95 }
96
SelectClientCertsGivenPreferred(const scoped_refptr<X509Certificate> & preferred_cert,const CertificateList & regular_certs,const SSLCertRequestInfo & request,ClientCertIdentityList * selected_certs)97 bool SelectClientCertsGivenPreferred(
98 const scoped_refptr<X509Certificate>& preferred_cert,
99 const CertificateList& regular_certs,
100 const SSLCertRequestInfo& request,
101 ClientCertIdentityList* selected_certs) {
102 auto preferred_identity = std::make_unique<ClientCertIdentityMac>(
103 preferred_cert, base::apple::ScopedCFTypeRef<SecIdentityRef>());
104
105 return store_.SelectClientCertsGivenPreferredForTesting(
106 std::move(preferred_identity),
107 ClientCertIdentityMacListFromCertificateList(regular_certs), request,
108 selected_certs);
109 }
110
111 private:
112 ClientCertStoreMac store_;
113 };
114
115 // Verify that the preferred cert gets filtered out when it doesn't match the
116 // server criteria.
TEST_F(ClientCertStoreMacTest,FilterOutThePreferredCert)117 TEST_F(ClientCertStoreMacTest, FilterOutThePreferredCert) {
118 scoped_refptr<X509Certificate> cert_1(
119 ImportCertFromFile(GetTestCertsDirectory(), "client_1.pem"));
120 ASSERT_TRUE(cert_1.get());
121
122 std::vector<std::string> authority_2(
123 1, std::string(reinterpret_cast<const char*>(kAuthority2DN),
124 sizeof(kAuthority2DN)));
125 EXPECT_FALSE(cert_1->IsIssuedByEncoded(authority_2));
126
127 std::vector<scoped_refptr<X509Certificate> > certs;
128 auto request = base::MakeRefCounted<SSLCertRequestInfo>();
129 request->cert_authorities = authority_2;
130
131 ClientCertIdentityList selected_certs;
132 bool rv = SelectClientCertsGivenPreferred(
133 cert_1, certs, *request.get(), &selected_certs);
134 EXPECT_TRUE(rv);
135 EXPECT_EQ(0u, selected_certs.size());
136 }
137
138 // Verify that the preferred cert takes the first position in the output list,
139 // when it does not get filtered out.
TEST_F(ClientCertStoreMacTest,PreferredCertGoesFirst)140 TEST_F(ClientCertStoreMacTest, PreferredCertGoesFirst) {
141 scoped_refptr<X509Certificate> cert_1(
142 ImportCertFromFile(GetTestCertsDirectory(), "client_1.pem"));
143 ASSERT_TRUE(cert_1.get());
144 scoped_refptr<X509Certificate> cert_2(
145 ImportCertFromFile(GetTestCertsDirectory(), "client_2.pem"));
146 ASSERT_TRUE(cert_2.get());
147
148 std::vector<scoped_refptr<X509Certificate> > certs;
149 certs.push_back(cert_2);
150 auto request = base::MakeRefCounted<SSLCertRequestInfo>();
151
152 ClientCertIdentityList selected_certs;
153 bool rv = SelectClientCertsGivenPreferred(
154 cert_1, certs, *request.get(), &selected_certs);
155 EXPECT_TRUE(rv);
156 ASSERT_EQ(2u, selected_certs.size());
157 EXPECT_TRUE(
158 selected_certs[0]->certificate()->EqualsExcludingChain(cert_1.get()));
159 EXPECT_TRUE(
160 selected_certs[1]->certificate()->EqualsExcludingChain(cert_2.get()));
161 }
162
TEST_F(ClientCertStoreMacTest,CertSupportsClientAuth)163 TEST_F(ClientCertStoreMacTest, CertSupportsClientAuth) {
164 base::FilePath certs_dir = GetTestCertsDirectory();
165 std::unique_ptr<CertBuilder> builder =
166 CertBuilder::FromFile(certs_dir.AppendASCII("ok_cert.pem"), nullptr);
167 ASSERT_TRUE(builder);
168
169 struct {
170 bool expected_result;
171 std::vector<bssl::KeyUsageBit> key_usages;
172 std::vector<bssl::der::Input> ekus;
173 } cases[] = {
174 {true, {}, {}},
175 {true, {bssl::KEY_USAGE_BIT_DIGITAL_SIGNATURE}, {}},
176 {true,
177 {bssl::KEY_USAGE_BIT_DIGITAL_SIGNATURE,
178 bssl::KEY_USAGE_BIT_KEY_CERT_SIGN},
179 {}},
180 {false, {bssl::KEY_USAGE_BIT_NON_REPUDIATION}, {}},
181 {false, {bssl::KEY_USAGE_BIT_KEY_ENCIPHERMENT}, {}},
182 {false, {bssl::KEY_USAGE_BIT_DATA_ENCIPHERMENT}, {}},
183 {false, {bssl::KEY_USAGE_BIT_KEY_AGREEMENT}, {}},
184 {false, {bssl::KEY_USAGE_BIT_KEY_CERT_SIGN}, {}},
185 {false, {bssl::KEY_USAGE_BIT_CRL_SIGN}, {}},
186 {false, {bssl::KEY_USAGE_BIT_ENCIPHER_ONLY}, {}},
187 {false, {bssl::KEY_USAGE_BIT_DECIPHER_ONLY}, {}},
188 {true, {}, {bssl::der::Input(bssl::kAnyEKU)}},
189 {true, {}, {bssl::der::Input(bssl::kClientAuth)}},
190 {true,
191 {},
192 {bssl::der::Input(bssl::kServerAuth),
193 bssl::der::Input(bssl::kClientAuth)}},
194 {true,
195 {},
196 {bssl::der::Input(bssl::kClientAuth),
197 bssl::der::Input(bssl::kServerAuth)}},
198 {false, {}, {bssl::der::Input(bssl::kServerAuth)}},
199 {true,
200 {bssl::KEY_USAGE_BIT_DIGITAL_SIGNATURE},
201 {bssl::der::Input(bssl::kClientAuth)}},
202 {false,
203 {bssl::KEY_USAGE_BIT_KEY_CERT_SIGN},
204 {bssl::der::Input(bssl::kClientAuth)}},
205 {false,
206 {bssl::KEY_USAGE_BIT_DIGITAL_SIGNATURE},
207 {bssl::der::Input(bssl::kServerAuth)}},
208 };
209
210 for (const auto& testcase : cases) {
211 SCOPED_TRACE(testcase.expected_result);
212 SCOPED_TRACE(KeyUsageVectorToString(testcase.key_usages));
213 SCOPED_TRACE(InputVectorToString(testcase.ekus));
214
215 if (testcase.key_usages.empty())
216 builder->EraseExtension(bssl::der::Input(bssl::kKeyUsageOid));
217 else
218 builder->SetKeyUsages(testcase.key_usages);
219
220 if (testcase.ekus.empty())
221 builder->EraseExtension(bssl::der::Input(bssl::kExtKeyUsageOid));
222 else
223 builder->SetExtendedKeyUsages(testcase.ekus);
224
225 auto request = base::MakeRefCounted<SSLCertRequestInfo>();
226 ClientCertIdentityList selected_certs;
227 bool rv = SelectClientCerts({builder->GetX509Certificate()}, *request.get(),
228 &selected_certs);
229 EXPECT_TRUE(rv);
230 if (testcase.expected_result) {
231 ASSERT_EQ(1U, selected_certs.size());
232 EXPECT_TRUE(selected_certs[0]->certificate()->EqualsExcludingChain(
233 builder->GetX509Certificate().get()));
234 } else {
235 EXPECT_TRUE(selected_certs.empty());
236 }
237 }
238 }
239
240 } // namespace net
241