xref: /aosp_15_r20/external/cronet/net/ssl/client_cert_store_mac_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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