blob: 3a2035e3c3953e2a9f38adf87444f864acbdb298 [file] [log] [blame]
Yingdi Yu0b60e7a2015-07-16 21:05:11 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesaventodef60f12017-09-17 17:26:07 -04002/*
Yingdi Yufe4733a2015-10-22 14:24:12 -07003 * Copyright (c) 2013-2017 Regents of the University of California.
Yingdi Yu0b60e7a2015-07-16 21:05:11 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6 *
7 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20 */
21
22#include "back-end-osx.hpp"
23#include "key-handle-osx.hpp"
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070024#include "tpm.hpp"
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -040025#include "../transform/private-key.hpp"
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070026
27#include <CoreServices/CoreServices.h>
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070028#include <Security/SecDigestTransform.h>
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -040029#include <Security/SecRandom.h>
30#include <Security/Security.h>
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070031
32namespace ndn {
33namespace security {
34namespace tpm {
35
Junxiao Shi0b1b4672017-07-02 22:02:31 -070036using util::CFReleaser;
37
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070038class BackEndOsx::Impl
39{
40public:
41 Impl()
42 : isTerminalMode(false)
43 {
44 }
45
46 /**
47 * @brief Get private key reference with name @p keyName.
48 *
49 * @param keyName
50 * @returns reference to the key
51 */
52 CFReleaser<SecKeychainItemRef>
53 getKey(const Name& keyName)
54 {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -040055 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(nullptr, keyName.toUri().c_str(),
56 kCFStringEncodingUTF8);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070057
58 CFReleaser<CFMutableDictionaryRef> attrDict =
59 CFDictionaryCreateMutable(nullptr, 5, &kCFTypeDictionaryKeyCallBacks, nullptr);
60
61 CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
62 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
63 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, kSecAttrKeyClassPrivate);
64 CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
65
66 CFReleaser<SecKeychainItemRef> keyItem;
67 // C-style cast is used as per Apple convention
68 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&keyItem.get());
69 keyItem.retain();
70
71 if (res != errSecSuccess) {
72 if (res == errSecAuthFailed) {
73 BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
74 }
75 BOOST_THROW_EXCEPTION(std::domain_error("Key does not exist"));
76 }
77
78 return keyItem;
79 }
80
81public:
82 SecKeychainRef keyChainRef;
83 bool isTerminalMode;
84};
85
86
87static CFTypeRef
88getAsymKeyType(KeyType keyType)
89{
90 switch (keyType) {
91 case KeyType::RSA:
92 return kSecAttrKeyTypeRSA;
93 case KeyType::EC:
94 return kSecAttrKeyTypeECDSA;
95 default:
96 BOOST_THROW_EXCEPTION(Tpm::Error("Unsupported key type"));
97 }
98}
99
100static CFTypeRef
101getDigestAlgorithm(DigestAlgorithm digestAlgo)
102{
103 switch (digestAlgo) {
Davide Pesaventodef60f12017-09-17 17:26:07 -0400104 case DigestAlgorithm::SHA224:
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700105 case DigestAlgorithm::SHA256:
Davide Pesaventodef60f12017-09-17 17:26:07 -0400106 case DigestAlgorithm::SHA384:
107 case DigestAlgorithm::SHA512:
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700108 return kSecDigestSHA2;
109 default:
110 return 0;
111 }
112}
113
114static long
115getDigestSize(DigestAlgorithm digestAlgo)
116{
117 switch (digestAlgo) {
Davide Pesaventodef60f12017-09-17 17:26:07 -0400118 case DigestAlgorithm::SHA224:
119 return 224;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700120 case DigestAlgorithm::SHA256:
121 return 256;
Davide Pesaventodef60f12017-09-17 17:26:07 -0400122 case DigestAlgorithm::SHA384:
123 return 384;
124 case DigestAlgorithm::SHA512:
125 return 512;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700126 default:
127 return -1;
128 }
129}
130
Yingdi Yufe4733a2015-10-22 14:24:12 -0700131BackEndOsx::BackEndOsx(const std::string&)
Davide Pesaventodef60f12017-09-17 17:26:07 -0400132 : m_impl(make_unique<Impl>())
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700133{
134 SecKeychainSetUserInteractionAllowed(!m_impl->isTerminalMode);
135
136 OSStatus res = SecKeychainCopyDefault(&m_impl->keyChainRef);
137
138 if (res == errSecNoDefaultKeychain) { //If no default key chain, create one.
139 BOOST_THROW_EXCEPTION(Error("No default keychain, create one first"));
140 }
141}
142
143BackEndOsx::~BackEndOsx() = default;
144
Yingdi Yufe4733a2015-10-22 14:24:12 -0700145const std::string&
146BackEndOsx::getScheme()
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700147{
Yingdi Yufe4733a2015-10-22 14:24:12 -0700148 static std::string scheme = "tpm-osxkeychain";
149 return scheme;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700150}
151
152bool
153BackEndOsx::isTerminalMode() const
154{
155 return m_impl->isTerminalMode;
156}
157
Yingdi Yufe4733a2015-10-22 14:24:12 -0700158void
159BackEndOsx::setTerminalMode(bool isTerminal) const
160{
161 m_impl->isTerminalMode = isTerminal;
162 SecKeychainSetUserInteractionAllowed(!isTerminal);
163}
164
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700165bool
Yingdi Yufe4733a2015-10-22 14:24:12 -0700166BackEndOsx::isTpmLocked() const
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700167{
168 SecKeychainStatus keychainStatus;
169
170 OSStatus res = SecKeychainGetStatus(m_impl->keyChainRef, &keychainStatus);
171 if (res != errSecSuccess)
172 return true;
173 else
174 return ((kSecUnlockStateStatus & keychainStatus) == 0);
175}
176
177bool
Yingdi Yufe4733a2015-10-22 14:24:12 -0700178BackEndOsx::unlockTpm(const char* pw, size_t pwLen) const
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700179{
180 // If the default key chain is already unlocked, return immediately.
Yingdi Yufe4733a2015-10-22 14:24:12 -0700181 if (!isTpmLocked())
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700182 return true;
183
184 if (m_impl->isTerminalMode) {
185 // Use the supplied password.
Yingdi Yufe4733a2015-10-22 14:24:12 -0700186 SecKeychainUnlock(m_impl->keyChainRef, pwLen, pw, true);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700187 }
188 else {
189 // If inTerminal is not set, get the password from GUI.
190 SecKeychainUnlock(m_impl->keyChainRef, 0, nullptr, false);
191 }
192
Yingdi Yufe4733a2015-10-22 14:24:12 -0700193 return !isTpmLocked();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700194}
195
196ConstBufferPtr
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400197BackEndOsx::sign(const KeyRefOsx& key, DigestAlgorithm digestAlgo, const uint8_t* buf, size_t size)
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700198{
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700199 CFReleaser<CFErrorRef> error;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700200 CFReleaser<SecTransformRef> signer = SecSignTransformCreate(key.get(), &error.get());
201 if (error != nullptr) {
202 BOOST_THROW_EXCEPTION(Error("Fail to create signer"));
203 }
204
205 // Set input
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400206 CFReleaser<CFDataRef> dataRef = CFDataCreateWithBytesNoCopy(nullptr, buf, size, kCFAllocatorNull);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700207 SecTransformSetAttribute(signer.get(), kSecTransformInputAttributeName, dataRef.get(), &error.get());
208 if (error != nullptr) {
209 BOOST_THROW_EXCEPTION(Error("Fail to configure input of signer"));
210 }
211
212 // Enable use of padding
213 SecTransformSetAttribute(signer.get(), kSecPaddingKey, kSecPaddingPKCS1Key, &error.get());
214 if (error != nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400215 BOOST_THROW_EXCEPTION(Error("Fail to configure padding of signer"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700216 }
217
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400218 // Set digest type
219 SecTransformSetAttribute(signer.get(), kSecDigestTypeAttribute, getDigestAlgorithm(digestAlgo), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700220 if (error != nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400221 BOOST_THROW_EXCEPTION(Error("Fail to configure digest type of signer"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700222 }
223
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400224 // Set digest length
225 long digestSize = getDigestSize(digestAlgo);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700226 CFReleaser<CFNumberRef> cfDigestSize = CFNumberCreate(nullptr, kCFNumberLongType, &digestSize);
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400227 SecTransformSetAttribute(signer.get(), kSecDigestLengthAttribute, cfDigestSize.get(), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700228 if (error != nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400229 BOOST_THROW_EXCEPTION(Error("Fail to configure digest length of signer"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700230 }
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400231
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700232 // Actually sign
233 // C-style cast is used as per Apple convention
234 CFReleaser<CFDataRef> signature = (CFDataRef)SecTransformExecute(signer.get(), &error.get());
235 if (error != nullptr) {
236 CFShow(error.get());
237 BOOST_THROW_EXCEPTION(Error("Fail to sign data"));
238 }
239
240 if (signature == nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400241 BOOST_THROW_EXCEPTION(Error("Signature is null"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700242 }
243
244 return make_shared<Buffer>(CFDataGetBytePtr(signature.get()), CFDataGetLength(signature.get()));
245}
246
247ConstBufferPtr
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400248BackEndOsx::decrypt(const KeyRefOsx& key, const uint8_t* cipherText, size_t cipherSize)
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700249{
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700250 CFReleaser<CFErrorRef> error;
251 CFReleaser<SecTransformRef> decryptor = SecDecryptTransformCreate(key.get(), &error.get());
252 if (error != nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400253 BOOST_THROW_EXCEPTION(Error("Fail to create decryptor"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700254 }
255
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400256 CFReleaser<CFDataRef> dataRef = CFDataCreateWithBytesNoCopy(nullptr, cipherText, cipherSize, kCFAllocatorNull);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700257 SecTransformSetAttribute(decryptor.get(), kSecTransformInputAttributeName, dataRef.get(), &error.get());
258 if (error != nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400259 BOOST_THROW_EXCEPTION(Error("Fail to configure decryptor input"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700260 }
261
262 SecTransformSetAttribute(decryptor.get(), kSecPaddingKey, kSecPaddingOAEPKey, &error.get());
263 if (error != nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400264 BOOST_THROW_EXCEPTION(Error("Fail to configure decryptor padding"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700265 }
266
267 CFReleaser<CFDataRef> output = (CFDataRef)SecTransformExecute(decryptor.get(), &error.get());
268 if (error != nullptr) {
269 // CFShow(error);
270 BOOST_THROW_EXCEPTION(Error("Fail to decrypt data"));
271 }
272
273 if (output == nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400274 BOOST_THROW_EXCEPTION(Error("Output is null"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700275 }
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400276
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700277 return make_shared<Buffer>(CFDataGetBytePtr(output.get()), CFDataGetLength(output.get()));
278}
279
280ConstBufferPtr
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400281BackEndOsx::derivePublicKey(const KeyRefOsx& key)
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700282{
283 CFReleaser<CFDataRef> exportedKey;
284 OSStatus res = SecItemExport(key.get(), // secItemOrArray
285 kSecFormatOpenSSL, // outputFormat
286 0, // flags
287 nullptr, // keyParams
288 &exportedKey.get()); // exportedData
289
290 if (res != errSecSuccess) {
291 if (res == errSecAuthFailed) {
292 BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
293 }
294 else {
295 BOOST_THROW_EXCEPTION(Error("Fail to export private key"));
296 }
297 }
298
299 transform::PrivateKey privateKey;
300 privateKey.loadPkcs1(CFDataGetBytePtr(exportedKey.get()), CFDataGetLength(exportedKey.get()));
301 return privateKey.derivePublicKey();
302}
303
304bool
305BackEndOsx::doHasKey(const Name& keyName) const
306{
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400307 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(nullptr, keyName.toUri().c_str(),
308 kCFStringEncodingUTF8);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700309
310 CFReleaser<CFMutableDictionaryRef> attrDict =
311 CFDictionaryCreateMutable(nullptr, 4, &kCFTypeDictionaryKeyCallBacks, nullptr);
312
313 CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
314 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
315 CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
316
317 CFReleaser<SecKeychainItemRef> itemRef;
318 // C-style cast is used as per Apple convention
319 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&itemRef.get());
320 itemRef.retain();
321
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400322 return res == errSecSuccess;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700323}
324
325unique_ptr<KeyHandle>
326BackEndOsx::doGetKeyHandle(const Name& keyName) const
327{
328 CFReleaser<SecKeychainItemRef> keyItem;
329 try {
330 keyItem = m_impl->getKey(keyName);
331 }
332 catch (const std::domain_error&) {
333 return nullptr;
334 }
335
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400336 return make_unique<KeyHandleOsx>((SecKeyRef)keyItem.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700337}
338
339unique_ptr<KeyHandle>
340BackEndOsx::doCreateKey(const Name& identityName, const KeyParams& params)
341{
342 KeyType keyType = params.getKeyType();
343 uint32_t keySize;
344 switch (keyType) {
345 case KeyType::RSA: {
346 const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(params);
347 keySize = rsaParams.getKeySize();
348 break;
349 }
350 case KeyType::EC: {
Spyridon Mastorakis1ece2e32015-08-27 18:52:21 -0700351 const EcKeyParams& ecParams = static_cast<const EcKeyParams&>(params);
352 keySize = ecParams.getKeySize();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700353 break;
354 }
355 default: {
356 BOOST_THROW_EXCEPTION(Tpm::Error("Fail to create a key pair: Unsupported key type"));
357 }
358 }
359 CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(nullptr, kCFNumberIntType, &keySize);
360
361 CFReleaser<CFMutableDictionaryRef> attrDict =
362 CFDictionaryCreateMutable(nullptr, 2, &kCFTypeDictionaryKeyCallBacks, nullptr);
363 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, getAsymKeyType(keyType));
364 CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
365
366 KeyRefOsx publicKey, privateKey;
367 // C-style cast is used as per Apple convention
368 OSStatus res = SecKeyGeneratePair((CFDictionaryRef)attrDict.get(), &publicKey.get(), &privateKey.get());
369
370 BOOST_ASSERT(privateKey != nullptr);
371
372 publicKey.retain();
373 privateKey.retain();
374
375 BOOST_ASSERT(privateKey != nullptr);
376
377 if (res != errSecSuccess) {
378 if (res == errSecAuthFailed) {
379 BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
380 }
381 else {
382 BOOST_THROW_EXCEPTION(Error("Fail to create a key pair"));
383 }
384 }
385
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400386 unique_ptr<KeyHandle> keyHandle = make_unique<KeyHandleOsx>(privateKey.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700387 setKeyName(*keyHandle, identityName, params);
388
389 SecKeychainAttribute attrs[1]; // maximum number of attributes
390 SecKeychainAttributeList attrList = { 0, attrs };
391 std::string keyUri = keyHandle->getKeyName().toUri();
392 {
393 attrs[attrList.count].tag = kSecKeyPrintName;
394 attrs[attrList.count].length = keyUri.size();
395 attrs[attrList.count].data = const_cast<char*>(keyUri.data());
396 attrList.count++;
397 }
398
399 SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)privateKey.get(), &attrList, 0, nullptr);
400 SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)publicKey.get(), &attrList, 0, nullptr);
401
402 return keyHandle;
403}
404
405void
406BackEndOsx::doDeleteKey(const Name& keyName)
407{
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400408 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(nullptr, keyName.toUri().c_str(),
409 kCFStringEncodingUTF8);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700410
411 CFReleaser<CFMutableDictionaryRef> searchDict =
412 CFDictionaryCreateMutable(nullptr, 5, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
413
414 CFDictionaryAddValue(searchDict.get(), kSecClass, kSecClassKey);
415 CFDictionaryAddValue(searchDict.get(), kSecAttrLabel, keyLabel.get());
416 CFDictionaryAddValue(searchDict.get(), kSecMatchLimit, kSecMatchLimitAll);
417 OSStatus res = SecItemDelete(searchDict.get());
418
419 if (res != errSecSuccess) {
420 if (res == errSecAuthFailed) {
421 BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
422 }
423 else if (res != errSecItemNotFound) {
424 BOOST_THROW_EXCEPTION(Error("Fail to delete a key pair"));
425 }
426 }
427}
428
429ConstBufferPtr
430BackEndOsx::doExportKey(const Name& keyName, const char* pw, size_t pwLen)
431{
432 CFReleaser<SecKeychainItemRef> privateKey;
433
434 try {
435 privateKey = m_impl->getKey(keyName);
436 }
437 catch (const std::domain_error&) {
438 BOOST_THROW_EXCEPTION(Tpm::Error("Private key does not exist in OSX Keychain"));
439 }
440
441 CFReleaser<CFDataRef> exportedKey;
442 SecItemImportExportKeyParameters keyParams;
443 memset(&keyParams, 0, sizeof(keyParams));
444 CFReleaser<CFStringRef> passphrase =
445 CFStringCreateWithBytes(0, reinterpret_cast<const uint8_t*>(pw), pwLen, kCFStringEncodingUTF8, false);
446 keyParams.passphrase = passphrase.get();
447 OSStatus res = SecItemExport(privateKey.get(), // secItemOrArray
448 kSecFormatWrappedPKCS8, // outputFormat
449 0, // flags
450 &keyParams, // keyParams
451 &exportedKey.get()); // exportedData
452
453 if (res != errSecSuccess) {
454 if (res == errSecAuthFailed) {
455 BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
456 }
457 else {
458 BOOST_THROW_EXCEPTION(Error("Fail to export private key"));
459 }
460 }
461
462 return make_shared<Buffer>(CFDataGetBytePtr(exportedKey.get()), CFDataGetLength(exportedKey.get()));
463}
464
465void
466BackEndOsx::doImportKey(const Name& keyName, const uint8_t* buf, size_t size,
467 const char* pw, size_t pwLen)
468{
469 CFReleaser<CFDataRef> importedKey = CFDataCreateWithBytesNoCopy(nullptr, buf, size, kCFAllocatorNull);
470
471 SecExternalFormat externalFormat = kSecFormatWrappedPKCS8;
472 SecExternalItemType externalType = kSecItemTypePrivateKey;
473
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400474 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(nullptr, keyName.toUri().c_str(),
475 kCFStringEncodingUTF8);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700476 CFReleaser<CFStringRef> passphrase =
477 CFStringCreateWithBytes(nullptr, reinterpret_cast<const uint8_t*>(pw), pwLen, kCFStringEncodingUTF8, false);
478 CFReleaser<SecAccessRef> access;
479 SecAccessCreate(keyLabel.get(), nullptr, &access.get());
480
481 CFArrayRef attributes = nullptr;
482
483 const SecItemImportExportKeyParameters keyParams{
484 SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION, // version
485 0, // flags
486 passphrase.get(), // passphrase
487 nullptr, // alert title
488 nullptr, // alert prompt
489 access.get(), // access ref
490 nullptr, // key usage
491 attributes // key attributes
492 };
493
494 CFReleaser<CFArrayRef> outItems;
495 OSStatus res = SecItemImport(importedKey.get(), // importedData
496 nullptr, // fileNameOrExtension
497 &externalFormat, // inputFormat
498 &externalType, // itemType
499 0, // flags
500 &keyParams, // keyParams
501 m_impl->keyChainRef, // importKeychain
502 &outItems.get()); // outItems
503
504 if (res != errSecSuccess) {
505 if (res == errSecAuthFailed) {
506 BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
507 }
508 else {
509 BOOST_THROW_EXCEPTION(Error("Cannot import the private key"));
510 }
511 }
512
513 // C-style cast is used as per Apple convention
514 SecKeychainItemRef privateKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
515 SecKeychainAttribute attrs[1]; // maximum number of attributes
516 SecKeychainAttributeList attrList = { 0, attrs };
517 std::string keyUri = keyName.toUri();
518 {
519 attrs[attrList.count].tag = kSecKeyPrintName;
520 attrs[attrList.count].length = keyUri.size();
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400521 attrs[attrList.count].data = const_cast<char*>(keyUri.data());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700522 attrList.count++;
523 }
524
525 res = SecKeychainItemModifyAttributesAndData(privateKey, &attrList, 0, nullptr);
526}
527
528} // namespace tpm
529} // namespace security
530} // namespace ndn