• 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 #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