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 #include <string_view>
13
14 #include "base/apple/scoped_cftyperef.h"
15 #include "base/files/file_path.h"
16 #include "base/files/file_util.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/numerics/checked_math.h"
19 #include "base/test/task_environment.h"
20 #include "crypto/scoped_fake_apple_keychain_v2.h"
21 #include "crypto/signature_verifier.h"
22 #include "net/ssl/ssl_private_key.h"
23 #include "net/ssl/ssl_private_key_test_util.h"
24 #include "net/test/cert_test_util.h"
25 #include "net/test/test_data_directory.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 #include "third_party/boringssl/src/include/openssl/bytestring.h"
28 #include "third_party/boringssl/src/include/openssl/ec_key.h"
29 #include "third_party/boringssl/src/include/openssl/evp.h"
30 #include "third_party/boringssl/src/include/openssl/rsa.h"
31 #include "third_party/boringssl/src/include/openssl/ssl.h"
32
33 namespace net {
34
35 namespace {
36
37 struct TestKey {
38 const char* name;
39 const char* cert_file;
40 const char* key_file;
41 int type;
42 };
43
44 const TestKey kTestKeys[] = {
45 {"RSA", "client_1.pem", "client_1.pk8", EVP_PKEY_RSA},
46 {"ECDSA_P256", "client_4.pem", "client_4.pk8", EVP_PKEY_EC},
47 {"ECDSA_P384", "client_5.pem", "client_5.pk8", EVP_PKEY_EC},
48 {"ECDSA_P521", "client_6.pem", "client_6.pk8", EVP_PKEY_EC},
49 };
50
TestKeyToString(const testing::TestParamInfo<TestKey> & params)51 std::string TestKeyToString(const testing::TestParamInfo<TestKey>& params) {
52 return params.param.name;
53 }
54
SecKeyFromPKCS8(std::string_view pkcs8)55 base::apple::ScopedCFTypeRef<SecKeyRef> SecKeyFromPKCS8(
56 std::string_view pkcs8) {
57 CBS cbs;
58 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(pkcs8.data()), pkcs8.size());
59 bssl::UniquePtr<EVP_PKEY> openssl_key(EVP_parse_private_key(&cbs));
60 if (!openssl_key || CBS_len(&cbs) != 0)
61 return base::apple::ScopedCFTypeRef<SecKeyRef>();
62
63 // `SecKeyCreateWithData` expects PKCS#1 for RSA keys, and a concatenated
64 // format for EC keys. See `SecKeyCopyExternalRepresentation` for details.
65 CFStringRef key_type;
66 bssl::ScopedCBB cbb;
67 if (!CBB_init(cbb.get(), 0)) {
68 return base::apple::ScopedCFTypeRef<SecKeyRef>();
69 }
70 if (EVP_PKEY_id(openssl_key.get()) == EVP_PKEY_RSA) {
71 key_type = kSecAttrKeyTypeRSA;
72 if (!RSA_marshal_private_key(cbb.get(),
73 EVP_PKEY_get0_RSA(openssl_key.get()))) {
74 return base::apple::ScopedCFTypeRef<SecKeyRef>();
75 }
76 } else if (EVP_PKEY_id(openssl_key.get()) == EVP_PKEY_EC) {
77 key_type = kSecAttrKeyTypeECSECPrimeRandom;
78 const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(openssl_key.get());
79 size_t priv_len = EC_KEY_priv2oct(ec_key, nullptr, 0);
80 uint8_t* out;
81 if (priv_len == 0 ||
82 !EC_POINT_point2cbb(cbb.get(), EC_KEY_get0_group(ec_key),
83 EC_KEY_get0_public_key(ec_key),
84 POINT_CONVERSION_UNCOMPRESSED, nullptr) ||
85 !CBB_add_space(cbb.get(), &out, priv_len) ||
86 EC_KEY_priv2oct(ec_key, out, priv_len) != priv_len) {
87 return base::apple::ScopedCFTypeRef<SecKeyRef>();
88 }
89 } else {
90 return base::apple::ScopedCFTypeRef<SecKeyRef>();
91 }
92
93 base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> attrs(
94 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
95 &kCFTypeDictionaryKeyCallBacks,
96 &kCFTypeDictionaryValueCallBacks));
97 CFDictionarySetValue(attrs.get(), kSecAttrKeyClass, kSecAttrKeyClassPrivate);
98 CFDictionarySetValue(attrs.get(), kSecAttrKeyType, key_type);
99
100 base::apple::ScopedCFTypeRef<CFDataRef> data(
101 CFDataCreate(kCFAllocatorDefault, CBB_data(cbb.get()),
102 base::checked_cast<CFIndex>(CBB_len(cbb.get()))));
103
104 return base::apple::ScopedCFTypeRef<SecKeyRef>(
105 SecKeyCreateWithData(data.get(), attrs.get(), nullptr));
106 }
107
108 } // namespace
109
110 class SSLPlatformKeyMacTest : public testing::TestWithParam<TestKey> {};
111
TEST_P(SSLPlatformKeyMacTest,KeyMatches)112 TEST_P(SSLPlatformKeyMacTest, KeyMatches) {
113 base::test::TaskEnvironment task_environment;
114
115 const TestKey& test_key = GetParam();
116
117 // Load test data.
118 scoped_refptr<X509Certificate> cert =
119 ImportCertFromFile(GetTestCertsDirectory(), test_key.cert_file);
120 ASSERT_TRUE(cert);
121
122 std::string pkcs8;
123 base::FilePath pkcs8_path =
124 GetTestCertsDirectory().AppendASCII(test_key.key_file);
125 ASSERT_TRUE(base::ReadFileToString(pkcs8_path, &pkcs8));
126 base::apple::ScopedCFTypeRef<SecKeyRef> sec_key = SecKeyFromPKCS8(pkcs8);
127 ASSERT_TRUE(sec_key);
128
129 // Make an `SSLPrivateKey` backed by `sec_key`.
130 scoped_refptr<SSLPrivateKey> key =
131 CreateSSLPrivateKeyForSecKey(cert.get(), sec_key.get());
132 ASSERT_TRUE(key);
133
134 // Mac keys from the default provider are expected to support all algorithms.
135 EXPECT_EQ(SSLPrivateKey::DefaultAlgorithmPreferences(test_key.type, true),
136 key->GetAlgorithmPreferences());
137
138 TestSSLPrivateKeyMatches(key.get(), pkcs8);
139 }
140
141 INSTANTIATE_TEST_SUITE_P(All,
142 SSLPlatformKeyMacTest,
143 testing::ValuesIn(kTestKeys),
144 TestKeyToString);
145
146 namespace {
147
148 constexpr char kTestKeychainAccessGroup[] = "test-keychain-access-group";
149 constexpr crypto::SignatureVerifier::SignatureAlgorithm kAcceptableAlgos[] = {
150 crypto::SignatureVerifier::ECDSA_SHA256};
151
152 const crypto::UnexportableKeyProvider::Config config = {
153 .keychain_access_group = kTestKeychainAccessGroup,
154 };
155
156 } // namespace
157
158 // Tests that a SSLPrivateKey can be created from a
159 // crypto::UnexportableSigningKey.
TEST(UnexportableSSLPlatformKeyMacTest,Convert)160 TEST(UnexportableSSLPlatformKeyMacTest, Convert) {
161 crypto::ScopedFakeAppleKeychainV2 scoped_fake_apple_keychain_{
162 kTestKeychainAccessGroup};
163 // Create a crypto::UnexportableSigningKey and verify preconditions.
164 std::unique_ptr<crypto::UnexportableKeyProvider> provider =
165 crypto::GetUnexportableKeyProvider(config);
166 ASSERT_TRUE(provider);
167 std::unique_ptr<crypto::UnexportableSigningKey> unexportable_key =
168 provider->GenerateSigningKeySlowly(kAcceptableAlgos);
169 ASSERT_TRUE(unexportable_key);
170 SecKeyRef key_ref = unexportable_key->GetSecKeyRef();
171 EXPECT_TRUE(key_ref);
172
173 auto ssl_private_key = WrapUnexportableKey(*unexportable_key);
174 EXPECT_TRUE(ssl_private_key);
175 }
176
177 } // namespace net
178