• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 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/ssl_platform_key_mac.h"
6 
7 #include <CoreFoundation/CoreFoundation.h>
8 #include <Security/SecItem.h>
9 #include <Security/SecKey.h>
10 
11 #include <string>
12 
13 #include "base/apple/scoped_cftyperef.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/numerics/checked_math.h"
18 #include "base/strings/string_piece.h"
19 #include "base/test/task_environment.h"
20 #include "net/ssl/ssl_private_key.h"
21 #include "net/ssl/ssl_private_key_test_util.h"
22 #include "net/test/cert_test_util.h"
23 #include "net/test/test_data_directory.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "third_party/boringssl/src/include/openssl/bytestring.h"
26 #include "third_party/boringssl/src/include/openssl/ec_key.h"
27 #include "third_party/boringssl/src/include/openssl/evp.h"
28 #include "third_party/boringssl/src/include/openssl/rsa.h"
29 #include "third_party/boringssl/src/include/openssl/ssl.h"
30 
31 namespace net {
32 
33 namespace {
34 
35 struct TestKey {
36   const char* name;
37   const char* cert_file;
38   const char* key_file;
39   int type;
40 };
41 
42 const TestKey kTestKeys[] = {
43     {"RSA", "client_1.pem", "client_1.pk8", EVP_PKEY_RSA},
44     {"ECDSA_P256", "client_4.pem", "client_4.pk8", EVP_PKEY_EC},
45     {"ECDSA_P384", "client_5.pem", "client_5.pk8", EVP_PKEY_EC},
46     {"ECDSA_P521", "client_6.pem", "client_6.pk8", EVP_PKEY_EC},
47 };
48 
TestKeyToString(const testing::TestParamInfo<TestKey> & params)49 std::string TestKeyToString(const testing::TestParamInfo<TestKey>& params) {
50   return params.param.name;
51 }
52 
SecKeyFromPKCS8(base::StringPiece pkcs8)53 base::apple::ScopedCFTypeRef<SecKeyRef> SecKeyFromPKCS8(
54     base::StringPiece pkcs8) {
55   CBS cbs;
56   CBS_init(&cbs, reinterpret_cast<const uint8_t*>(pkcs8.data()), pkcs8.size());
57   bssl::UniquePtr<EVP_PKEY> openssl_key(EVP_parse_private_key(&cbs));
58   if (!openssl_key || CBS_len(&cbs) != 0)
59     return base::apple::ScopedCFTypeRef<SecKeyRef>();
60 
61   // `SecKeyCreateWithData` expects PKCS#1 for RSA keys, and a concatenated
62   // format for EC keys. See `SecKeyCopyExternalRepresentation` for details.
63   CFStringRef key_type;
64   bssl::ScopedCBB cbb;
65   if (!CBB_init(cbb.get(), 0)) {
66     return base::apple::ScopedCFTypeRef<SecKeyRef>();
67   }
68   if (EVP_PKEY_id(openssl_key.get()) == EVP_PKEY_RSA) {
69     key_type = kSecAttrKeyTypeRSA;
70     if (!RSA_marshal_private_key(cbb.get(),
71                                  EVP_PKEY_get0_RSA(openssl_key.get()))) {
72       return base::apple::ScopedCFTypeRef<SecKeyRef>();
73     }
74   } else if (EVP_PKEY_id(openssl_key.get()) == EVP_PKEY_EC) {
75     key_type = kSecAttrKeyTypeECSECPrimeRandom;
76     const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(openssl_key.get());
77     size_t priv_len = EC_KEY_priv2oct(ec_key, nullptr, 0);
78     uint8_t* out;
79     if (priv_len == 0 ||
80         !EC_POINT_point2cbb(cbb.get(), EC_KEY_get0_group(ec_key),
81                             EC_KEY_get0_public_key(ec_key),
82                             POINT_CONVERSION_UNCOMPRESSED, nullptr) ||
83         !CBB_add_space(cbb.get(), &out, priv_len) ||
84         EC_KEY_priv2oct(ec_key, out, priv_len) != priv_len) {
85       return base::apple::ScopedCFTypeRef<SecKeyRef>();
86     }
87   } else {
88     return base::apple::ScopedCFTypeRef<SecKeyRef>();
89   }
90 
91   base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> attrs(
92       CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
93                                 &kCFTypeDictionaryKeyCallBacks,
94                                 &kCFTypeDictionaryValueCallBacks));
95   CFDictionarySetValue(attrs.get(), kSecAttrKeyClass, kSecAttrKeyClassPrivate);
96   CFDictionarySetValue(attrs.get(), kSecAttrKeyType, key_type);
97 
98   base::apple::ScopedCFTypeRef<CFDataRef> data(
99       CFDataCreate(kCFAllocatorDefault, CBB_data(cbb.get()),
100                    base::checked_cast<CFIndex>(CBB_len(cbb.get()))));
101 
102   return base::apple::ScopedCFTypeRef<SecKeyRef>(
103       SecKeyCreateWithData(data.get(), attrs.get(), nullptr));
104 }
105 
106 }  // namespace
107 
108 class SSLPlatformKeyMacTest : public testing::TestWithParam<TestKey> {};
109 
TEST_P(SSLPlatformKeyMacTest,KeyMatches)110 TEST_P(SSLPlatformKeyMacTest, KeyMatches) {
111   base::test::TaskEnvironment task_environment;
112 
113   const TestKey& test_key = GetParam();
114 
115   // Load test data.
116   scoped_refptr<X509Certificate> cert =
117       ImportCertFromFile(GetTestCertsDirectory(), test_key.cert_file);
118   ASSERT_TRUE(cert);
119 
120   std::string pkcs8;
121   base::FilePath pkcs8_path =
122       GetTestCertsDirectory().AppendASCII(test_key.key_file);
123   ASSERT_TRUE(base::ReadFileToString(pkcs8_path, &pkcs8));
124   base::apple::ScopedCFTypeRef<SecKeyRef> sec_key = SecKeyFromPKCS8(pkcs8);
125   ASSERT_TRUE(sec_key);
126 
127   // Make an `SSLPrivateKey` backed by `sec_key`.
128   scoped_refptr<SSLPrivateKey> key =
129       CreateSSLPrivateKeyForSecKey(cert.get(), sec_key.get());
130   ASSERT_TRUE(key);
131 
132   // Mac keys from the default provider are expected to support all algorithms.
133   EXPECT_EQ(SSLPrivateKey::DefaultAlgorithmPreferences(test_key.type, true),
134             key->GetAlgorithmPreferences());
135 
136   TestSSLPrivateKeyMatches(key.get(), pkcs8);
137 }
138 
139 INSTANTIATE_TEST_SUITE_P(All,
140                          SSLPlatformKeyMacTest,
141                          testing::ValuesIn(kTestKeys),
142                          TestKeyToString);
143 
144 }  // namespace net
145