blob: 139b7d2e26f99a05acc7fc5331212568e96fe4b7 [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/*
Davide Pesavento5ee8ec02018-09-01 19:06:12 -04003 * Copyright (c) 2013-2018 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"
Davide Pesavento5ee8ec02018-09-01 19:06:12 -040026#include "../../util/cf-string-osx.hpp"
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070027
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -040028#include <Security/Security.h>
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070029
30namespace ndn {
31namespace security {
32namespace tpm {
33
Davide Pesavento5ee8ec02018-09-01 19:06:12 -040034namespace cfstring = util::cfstring;
Junxiao Shi0b1b4672017-07-02 22:02:31 -070035using util::CFReleaser;
36
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070037class BackEndOsx::Impl
38{
39public:
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070040 SecKeychainRef keyChainRef;
Davide Pesavento5ee8ec02018-09-01 19:06:12 -040041 bool isTerminalMode = false;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070042};
43
Davide Pesavento5ee8ec02018-09-01 19:06:12 -040044static CFReleaser<CFDataRef>
45makeCFDataNoCopy(const uint8_t* buf, size_t buflen)
46{
47 return CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buf, buflen, kCFAllocatorNull);
48}
49
50static CFReleaser<CFMutableDictionaryRef>
51makeCFMutableDictionary()
52{
53 return CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
54 &kCFTypeDictionaryKeyCallBacks,
55 &kCFTypeDictionaryValueCallBacks);
56}
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070057
58static CFTypeRef
59getAsymKeyType(KeyType keyType)
60{
61 switch (keyType) {
62 case KeyType::RSA:
63 return kSecAttrKeyTypeRSA;
64 case KeyType::EC:
65 return kSecAttrKeyTypeECDSA;
66 default:
67 BOOST_THROW_EXCEPTION(Tpm::Error("Unsupported key type"));
68 }
69}
70
71static CFTypeRef
72getDigestAlgorithm(DigestAlgorithm digestAlgo)
73{
74 switch (digestAlgo) {
Davide Pesaventodef60f12017-09-17 17:26:07 -040075 case DigestAlgorithm::SHA224:
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070076 case DigestAlgorithm::SHA256:
Davide Pesaventodef60f12017-09-17 17:26:07 -040077 case DigestAlgorithm::SHA384:
78 case DigestAlgorithm::SHA512:
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070079 return kSecDigestSHA2;
80 default:
Davide Pesavento5ee8ec02018-09-01 19:06:12 -040081 return nullptr;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070082 }
83}
84
85static long
86getDigestSize(DigestAlgorithm digestAlgo)
87{
88 switch (digestAlgo) {
Davide Pesaventodef60f12017-09-17 17:26:07 -040089 case DigestAlgorithm::SHA224:
90 return 224;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070091 case DigestAlgorithm::SHA256:
92 return 256;
Davide Pesaventodef60f12017-09-17 17:26:07 -040093 case DigestAlgorithm::SHA384:
94 return 384;
95 case DigestAlgorithm::SHA512:
96 return 512;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070097 default:
98 return -1;
99 }
100}
101
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400102/**
103 * @brief Get reference to private key with name @p keyName.
104 * @param keyName
105 */
106static CFReleaser<SecKeychainItemRef>
107getKey(const Name& keyName)
108{
109 auto keyLabel = cfstring::fromStdString(keyName.toUri());
110
111 auto attrDict = makeCFMutableDictionary();
112 CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
113 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
114 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, kSecAttrKeyClassPrivate);
115 CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
116
117 CFReleaser<SecKeychainItemRef> keyItem;
118 // C-style cast is used as per Apple convention
119 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&keyItem.get());
120 keyItem.retain();
121
122 if (res != errSecSuccess) {
123 if (res == errSecAuthFailed) {
124 BOOST_THROW_EXCEPTION(BackEnd::Error("Fail to unlock the keychain"));
125 }
126 return nullptr;
127 }
128
129 return keyItem;
130}
131
Yingdi Yufe4733a2015-10-22 14:24:12 -0700132BackEndOsx::BackEndOsx(const std::string&)
Davide Pesaventodef60f12017-09-17 17:26:07 -0400133 : m_impl(make_unique<Impl>())
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700134{
135 SecKeychainSetUserInteractionAllowed(!m_impl->isTerminalMode);
136
137 OSStatus res = SecKeychainCopyDefault(&m_impl->keyChainRef);
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400138 if (res == errSecNoDefaultKeychain) {
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700139 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;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700169 OSStatus res = SecKeychainGetStatus(m_impl->keyChainRef, &keychainStatus);
170 if (res != errSecSuccess)
171 return true;
172 else
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400173 return (kSecUnlockStateStatus & keychainStatus) == 0;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700174}
175
176bool
Yingdi Yufe4733a2015-10-22 14:24:12 -0700177BackEndOsx::unlockTpm(const char* pw, size_t pwLen) const
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700178{
179 // If the default key chain is already unlocked, return immediately.
Yingdi Yufe4733a2015-10-22 14:24:12 -0700180 if (!isTpmLocked())
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700181 return true;
182
183 if (m_impl->isTerminalMode) {
184 // Use the supplied password.
Yingdi Yufe4733a2015-10-22 14:24:12 -0700185 SecKeychainUnlock(m_impl->keyChainRef, pwLen, pw, true);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700186 }
187 else {
188 // If inTerminal is not set, get the password from GUI.
189 SecKeychainUnlock(m_impl->keyChainRef, 0, nullptr, false);
190 }
191
Yingdi Yufe4733a2015-10-22 14:24:12 -0700192 return !isTpmLocked();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700193}
194
195ConstBufferPtr
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400196BackEndOsx::sign(const KeyRefOsx& key, DigestAlgorithm digestAlgo, const uint8_t* buf, size_t size)
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700197{
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700198 CFReleaser<CFErrorRef> error;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700199 CFReleaser<SecTransformRef> signer = SecSignTransformCreate(key.get(), &error.get());
200 if (error != nullptr) {
201 BOOST_THROW_EXCEPTION(Error("Fail to create signer"));
202 }
203
204 // Set input
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400205 auto data = makeCFDataNoCopy(buf, size);
206 SecTransformSetAttribute(signer.get(), kSecTransformInputAttributeName, data.get(), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700207 if (error != nullptr) {
208 BOOST_THROW_EXCEPTION(Error("Fail to configure input of signer"));
209 }
210
211 // Enable use of padding
212 SecTransformSetAttribute(signer.get(), kSecPaddingKey, kSecPaddingPKCS1Key, &error.get());
213 if (error != nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400214 BOOST_THROW_EXCEPTION(Error("Fail to configure padding of signer"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700215 }
216
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400217 // Set digest type
218 SecTransformSetAttribute(signer.get(), kSecDigestTypeAttribute, getDigestAlgorithm(digestAlgo), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700219 if (error != nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400220 BOOST_THROW_EXCEPTION(Error("Fail to configure digest type of signer"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700221 }
222
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400223 // Set digest length
224 long digestSize = getDigestSize(digestAlgo);
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400225 CFReleaser<CFNumberRef> cfDigestSize = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &digestSize);
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400226 SecTransformSetAttribute(signer.get(), kSecDigestLengthAttribute, cfDigestSize.get(), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700227 if (error != nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400228 BOOST_THROW_EXCEPTION(Error("Fail to configure digest length of signer"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700229 }
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400230
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700231 // Actually sign
232 // C-style cast is used as per Apple convention
233 CFReleaser<CFDataRef> signature = (CFDataRef)SecTransformExecute(signer.get(), &error.get());
234 if (error != nullptr) {
235 CFShow(error.get());
236 BOOST_THROW_EXCEPTION(Error("Fail to sign data"));
237 }
238
239 if (signature == nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400240 BOOST_THROW_EXCEPTION(Error("Signature is null"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700241 }
242
243 return make_shared<Buffer>(CFDataGetBytePtr(signature.get()), CFDataGetLength(signature.get()));
244}
245
246ConstBufferPtr
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400247BackEndOsx::decrypt(const KeyRefOsx& key, const uint8_t* cipherText, size_t cipherSize)
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700248{
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700249 CFReleaser<CFErrorRef> error;
250 CFReleaser<SecTransformRef> decryptor = SecDecryptTransformCreate(key.get(), &error.get());
251 if (error != nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400252 BOOST_THROW_EXCEPTION(Error("Fail to create decryptor"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700253 }
254
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400255 auto data = makeCFDataNoCopy(cipherText, cipherSize);
256 SecTransformSetAttribute(decryptor.get(), kSecTransformInputAttributeName, data.get(), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700257 if (error != nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400258 BOOST_THROW_EXCEPTION(Error("Fail to configure decryptor input"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700259 }
260
261 SecTransformSetAttribute(decryptor.get(), kSecPaddingKey, kSecPaddingOAEPKey, &error.get());
262 if (error != nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400263 BOOST_THROW_EXCEPTION(Error("Fail to configure decryptor padding"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700264 }
265
266 CFReleaser<CFDataRef> output = (CFDataRef)SecTransformExecute(decryptor.get(), &error.get());
267 if (error != nullptr) {
268 // CFShow(error);
269 BOOST_THROW_EXCEPTION(Error("Fail to decrypt data"));
270 }
271
272 if (output == nullptr) {
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400273 BOOST_THROW_EXCEPTION(Error("Output is null"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700274 }
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400275
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700276 return make_shared<Buffer>(CFDataGetBytePtr(output.get()), CFDataGetLength(output.get()));
277}
278
279ConstBufferPtr
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400280BackEndOsx::derivePublicKey(const KeyRefOsx& key)
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700281{
282 CFReleaser<CFDataRef> exportedKey;
283 OSStatus res = SecItemExport(key.get(), // secItemOrArray
284 kSecFormatOpenSSL, // outputFormat
285 0, // flags
286 nullptr, // keyParams
287 &exportedKey.get()); // exportedData
288
289 if (res != errSecSuccess) {
290 if (res == errSecAuthFailed) {
291 BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
292 }
293 else {
294 BOOST_THROW_EXCEPTION(Error("Fail to export private key"));
295 }
296 }
297
298 transform::PrivateKey privateKey;
299 privateKey.loadPkcs1(CFDataGetBytePtr(exportedKey.get()), CFDataGetLength(exportedKey.get()));
300 return privateKey.derivePublicKey();
301}
302
303bool
304BackEndOsx::doHasKey(const Name& keyName) const
305{
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400306 auto keyLabel = cfstring::fromStdString(keyName.toUri());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700307
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400308 auto attrDict = makeCFMutableDictionary();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700309 CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
310 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
311 CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
312
313 CFReleaser<SecKeychainItemRef> itemRef;
314 // C-style cast is used as per Apple convention
315 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&itemRef.get());
316 itemRef.retain();
317
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400318 return res == errSecSuccess;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700319}
320
321unique_ptr<KeyHandle>
322BackEndOsx::doGetKeyHandle(const Name& keyName) const
323{
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400324 CFReleaser<SecKeychainItemRef> keychainItem = getKey(keyName);
325 if (keychainItem == nullptr) {
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700326 return nullptr;
327 }
328
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400329 return make_unique<KeyHandleOsx>((SecKeyRef)keychainItem.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700330}
331
332unique_ptr<KeyHandle>
333BackEndOsx::doCreateKey(const Name& identityName, const KeyParams& params)
334{
335 KeyType keyType = params.getKeyType();
336 uint32_t keySize;
337 switch (keyType) {
338 case KeyType::RSA: {
339 const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(params);
340 keySize = rsaParams.getKeySize();
341 break;
342 }
343 case KeyType::EC: {
Spyridon Mastorakis1ece2e32015-08-27 18:52:21 -0700344 const EcKeyParams& ecParams = static_cast<const EcKeyParams&>(params);
345 keySize = ecParams.getKeySize();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700346 break;
347 }
348 default: {
349 BOOST_THROW_EXCEPTION(Tpm::Error("Fail to create a key pair: Unsupported key type"));
350 }
351 }
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400352 CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700353
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400354 auto attrDict = makeCFMutableDictionary();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700355 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, getAsymKeyType(keyType));
356 CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
357
358 KeyRefOsx publicKey, privateKey;
359 // C-style cast is used as per Apple convention
360 OSStatus res = SecKeyGeneratePair((CFDictionaryRef)attrDict.get(), &publicKey.get(), &privateKey.get());
361
362 BOOST_ASSERT(privateKey != nullptr);
363
364 publicKey.retain();
365 privateKey.retain();
366
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700367 if (res != errSecSuccess) {
368 if (res == errSecAuthFailed) {
369 BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
370 }
371 else {
372 BOOST_THROW_EXCEPTION(Error("Fail to create a key pair"));
373 }
374 }
375
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400376 unique_ptr<KeyHandle> keyHandle = make_unique<KeyHandleOsx>(privateKey.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700377 setKeyName(*keyHandle, identityName, params);
378
379 SecKeychainAttribute attrs[1]; // maximum number of attributes
380 SecKeychainAttributeList attrList = { 0, attrs };
381 std::string keyUri = keyHandle->getKeyName().toUri();
382 {
383 attrs[attrList.count].tag = kSecKeyPrintName;
384 attrs[attrList.count].length = keyUri.size();
385 attrs[attrList.count].data = const_cast<char*>(keyUri.data());
386 attrList.count++;
387 }
388
389 SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)privateKey.get(), &attrList, 0, nullptr);
390 SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)publicKey.get(), &attrList, 0, nullptr);
391
392 return keyHandle;
393}
394
395void
396BackEndOsx::doDeleteKey(const Name& keyName)
397{
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400398 auto keyLabel = cfstring::fromStdString(keyName.toUri());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700399
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400400 auto searchDict = makeCFMutableDictionary();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700401 CFDictionaryAddValue(searchDict.get(), kSecClass, kSecClassKey);
402 CFDictionaryAddValue(searchDict.get(), kSecAttrLabel, keyLabel.get());
403 CFDictionaryAddValue(searchDict.get(), kSecMatchLimit, kSecMatchLimitAll);
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400404
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700405 OSStatus res = SecItemDelete(searchDict.get());
406
407 if (res != errSecSuccess) {
408 if (res == errSecAuthFailed) {
409 BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
410 }
411 else if (res != errSecItemNotFound) {
412 BOOST_THROW_EXCEPTION(Error("Fail to delete a key pair"));
413 }
414 }
415}
416
417ConstBufferPtr
418BackEndOsx::doExportKey(const Name& keyName, const char* pw, size_t pwLen)
419{
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400420 CFReleaser<SecKeychainItemRef> keychainItem = getKey(keyName);
421 if (keychainItem == nullptr) {
422 BOOST_THROW_EXCEPTION(Error("Private key does not exist in macOS Keychain"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700423 }
424
425 CFReleaser<CFDataRef> exportedKey;
426 SecItemImportExportKeyParameters keyParams;
427 memset(&keyParams, 0, sizeof(keyParams));
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400428 auto passphrase = cfstring::fromBuffer(reinterpret_cast<const uint8_t*>(pw), pwLen);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700429 keyParams.passphrase = passphrase.get();
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400430
431 OSStatus res = SecItemExport(keychainItem.get(), // secItemOrArray
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700432 kSecFormatWrappedPKCS8, // outputFormat
433 0, // flags
434 &keyParams, // keyParams
435 &exportedKey.get()); // exportedData
436
437 if (res != errSecSuccess) {
438 if (res == errSecAuthFailed) {
439 BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
440 }
441 else {
442 BOOST_THROW_EXCEPTION(Error("Fail to export private key"));
443 }
444 }
445
446 return make_shared<Buffer>(CFDataGetBytePtr(exportedKey.get()), CFDataGetLength(exportedKey.get()));
447}
448
449void
450BackEndOsx::doImportKey(const Name& keyName, const uint8_t* buf, size_t size,
451 const char* pw, size_t pwLen)
452{
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400453 auto importedKey = makeCFDataNoCopy(buf, size);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700454
455 SecExternalFormat externalFormat = kSecFormatWrappedPKCS8;
456 SecExternalItemType externalType = kSecItemTypePrivateKey;
457
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400458 auto passphrase = cfstring::fromBuffer(reinterpret_cast<const uint8_t*>(pw), pwLen);
459 auto keyLabel = cfstring::fromStdString(keyName.toUri());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700460 CFReleaser<SecAccessRef> access;
461 SecAccessCreate(keyLabel.get(), nullptr, &access.get());
462
463 CFArrayRef attributes = nullptr;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700464 const SecItemImportExportKeyParameters keyParams{
465 SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION, // version
466 0, // flags
467 passphrase.get(), // passphrase
468 nullptr, // alert title
469 nullptr, // alert prompt
470 access.get(), // access ref
471 nullptr, // key usage
472 attributes // key attributes
473 };
474
475 CFReleaser<CFArrayRef> outItems;
476 OSStatus res = SecItemImport(importedKey.get(), // importedData
477 nullptr, // fileNameOrExtension
478 &externalFormat, // inputFormat
479 &externalType, // itemType
480 0, // flags
481 &keyParams, // keyParams
482 m_impl->keyChainRef, // importKeychain
483 &outItems.get()); // outItems
484
485 if (res != errSecSuccess) {
486 if (res == errSecAuthFailed) {
487 BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
488 }
489 else {
490 BOOST_THROW_EXCEPTION(Error("Cannot import the private key"));
491 }
492 }
493
494 // C-style cast is used as per Apple convention
495 SecKeychainItemRef privateKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
496 SecKeychainAttribute attrs[1]; // maximum number of attributes
497 SecKeychainAttributeList attrList = { 0, attrs };
498 std::string keyUri = keyName.toUri();
499 {
500 attrs[attrList.count].tag = kSecKeyPrintName;
501 attrs[attrList.count].length = keyUri.size();
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400502 attrs[attrList.count].data = const_cast<char*>(keyUri.data());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700503 attrList.count++;
504 }
505
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400506 SecKeychainItemModifyAttributesAndData(privateKey, &attrList, 0, nullptr);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700507}
508
509} // namespace tpm
510} // namespace security
511} // namespace ndn