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