blob: 15bd41aa504103dca218c2c87779c801237dd5ef [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>
Davide Pesaventofbda9332018-09-02 16:42:33 -040029#include <cstring>
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070030
31namespace ndn {
32namespace security {
33namespace tpm {
34
Davide Pesavento5ee8ec02018-09-01 19:06:12 -040035namespace cfstring = util::cfstring;
Junxiao Shi0b1b4672017-07-02 22:02:31 -070036using util::CFReleaser;
37
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070038class BackEndOsx::Impl
39{
40public:
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070041 SecKeychainRef keyChainRef;
Davide Pesavento5ee8ec02018-09-01 19:06:12 -040042 bool isTerminalMode = false;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070043};
44
Davide Pesavento5ee8ec02018-09-01 19:06:12 -040045static CFReleaser<CFDataRef>
46makeCFDataNoCopy(const uint8_t* buf, size_t buflen)
47{
48 return CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buf, buflen, kCFAllocatorNull);
49}
50
51static CFReleaser<CFMutableDictionaryRef>
52makeCFMutableDictionary()
53{
54 return CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
55 &kCFTypeDictionaryKeyCallBacks,
56 &kCFTypeDictionaryValueCallBacks);
57}
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070058
Davide Pesaventofbda9332018-09-02 16:42:33 -040059static std::string
60getErrorMessage(OSStatus status)
61{
62 CFReleaser<CFStringRef> msg = SecCopyErrorMessageString(status, nullptr);
63 if (msg != nullptr)
64 return cfstring::toStdString(msg.get());
65 else
66 return "<no error message>";
67}
68
69static std::string
70getFailureReason(CFErrorRef err)
71{
72 CFReleaser<CFStringRef> reason = CFErrorCopyFailureReason(err);
73 if (reason != nullptr)
74 return cfstring::toStdString(reason.get());
75 else
76 return "<unknown reason>";
77}
78
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070079static CFTypeRef
80getAsymKeyType(KeyType keyType)
81{
82 switch (keyType) {
83 case KeyType::RSA:
84 return kSecAttrKeyTypeRSA;
85 case KeyType::EC:
86 return kSecAttrKeyTypeECDSA;
87 default:
88 BOOST_THROW_EXCEPTION(Tpm::Error("Unsupported key type"));
89 }
90}
91
92static CFTypeRef
93getDigestAlgorithm(DigestAlgorithm digestAlgo)
94{
95 switch (digestAlgo) {
Davide Pesaventodef60f12017-09-17 17:26:07 -040096 case DigestAlgorithm::SHA224:
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070097 case DigestAlgorithm::SHA256:
Davide Pesaventodef60f12017-09-17 17:26:07 -040098 case DigestAlgorithm::SHA384:
99 case DigestAlgorithm::SHA512:
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700100 return kSecDigestSHA2;
101 default:
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400102 return nullptr;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700103 }
104}
105
Davide Pesaventofbda9332018-09-02 16:42:33 -0400106static int
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700107getDigestSize(DigestAlgorithm digestAlgo)
108{
109 switch (digestAlgo) {
Davide Pesaventodef60f12017-09-17 17:26:07 -0400110 case DigestAlgorithm::SHA224:
111 return 224;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700112 case DigestAlgorithm::SHA256:
113 return 256;
Davide Pesaventodef60f12017-09-17 17:26:07 -0400114 case DigestAlgorithm::SHA384:
115 return 384;
116 case DigestAlgorithm::SHA512:
117 return 512;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700118 default:
119 return -1;
120 }
121}
122
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400123/**
124 * @brief Get reference to private key with name @p keyName.
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400125 */
Davide Pesaventofbda9332018-09-02 16:42:33 -0400126static KeyRefOsx
127getKeyRef(const Name& keyName)
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400128{
129 auto keyLabel = cfstring::fromStdString(keyName.toUri());
130
Davide Pesaventofbda9332018-09-02 16:42:33 -0400131 auto query = makeCFMutableDictionary();
132 CFDictionaryAddValue(query.get(), kSecClass, kSecClassKey);
133 CFDictionaryAddValue(query.get(), kSecAttrKeyClass, kSecAttrKeyClassPrivate);
134 CFDictionaryAddValue(query.get(), kSecAttrLabel, keyLabel.get());
135 CFDictionaryAddValue(query.get(), kSecReturnRef, kCFBooleanTrue);
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400136
Davide Pesaventofbda9332018-09-02 16:42:33 -0400137 KeyRefOsx keyRef;
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400138 // C-style cast is used as per Apple convention
Davide Pesaventofbda9332018-09-02 16:42:33 -0400139 OSStatus res = SecItemCopyMatching(query.get(), (CFTypeRef*)&keyRef.get());
140 keyRef.retain();
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400141
Davide Pesaventofbda9332018-09-02 16:42:33 -0400142 if (res == errSecSuccess) {
143 return keyRef;
144 }
145 else if (res == errSecItemNotFound) {
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400146 return nullptr;
147 }
Davide Pesaventofbda9332018-09-02 16:42:33 -0400148 else {
149 BOOST_THROW_EXCEPTION(BackEnd::Error("Key lookup in keychain failed: " + getErrorMessage(res)));
150 }
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400151}
152
Yingdi Yufe4733a2015-10-22 14:24:12 -0700153BackEndOsx::BackEndOsx(const std::string&)
Davide Pesaventodef60f12017-09-17 17:26:07 -0400154 : m_impl(make_unique<Impl>())
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700155{
156 SecKeychainSetUserInteractionAllowed(!m_impl->isTerminalMode);
157
158 OSStatus res = SecKeychainCopyDefault(&m_impl->keyChainRef);
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400159 if (res == errSecNoDefaultKeychain) {
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700160 BOOST_THROW_EXCEPTION(Error("No default keychain, create one first"));
161 }
162}
163
164BackEndOsx::~BackEndOsx() = default;
165
Yingdi Yufe4733a2015-10-22 14:24:12 -0700166const std::string&
167BackEndOsx::getScheme()
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700168{
Yingdi Yufe4733a2015-10-22 14:24:12 -0700169 static std::string scheme = "tpm-osxkeychain";
170 return scheme;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700171}
172
173bool
174BackEndOsx::isTerminalMode() const
175{
176 return m_impl->isTerminalMode;
177}
178
Yingdi Yufe4733a2015-10-22 14:24:12 -0700179void
180BackEndOsx::setTerminalMode(bool isTerminal) const
181{
182 m_impl->isTerminalMode = isTerminal;
183 SecKeychainSetUserInteractionAllowed(!isTerminal);
184}
185
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700186bool
Yingdi Yufe4733a2015-10-22 14:24:12 -0700187BackEndOsx::isTpmLocked() const
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700188{
189 SecKeychainStatus keychainStatus;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700190 OSStatus res = SecKeychainGetStatus(m_impl->keyChainRef, &keychainStatus);
191 if (res != errSecSuccess)
192 return true;
193 else
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400194 return (kSecUnlockStateStatus & keychainStatus) == 0;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700195}
196
197bool
Yingdi Yufe4733a2015-10-22 14:24:12 -0700198BackEndOsx::unlockTpm(const char* pw, size_t pwLen) const
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700199{
200 // If the default key chain is already unlocked, return immediately.
Yingdi Yufe4733a2015-10-22 14:24:12 -0700201 if (!isTpmLocked())
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700202 return true;
203
204 if (m_impl->isTerminalMode) {
205 // Use the supplied password.
Yingdi Yufe4733a2015-10-22 14:24:12 -0700206 SecKeychainUnlock(m_impl->keyChainRef, pwLen, pw, true);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700207 }
208 else {
209 // If inTerminal is not set, get the password from GUI.
210 SecKeychainUnlock(m_impl->keyChainRef, 0, nullptr, false);
211 }
212
Yingdi Yufe4733a2015-10-22 14:24:12 -0700213 return !isTpmLocked();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700214}
215
216ConstBufferPtr
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400217BackEndOsx::sign(const KeyRefOsx& key, DigestAlgorithm digestAlgo, const uint8_t* buf, size_t size)
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700218{
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700219 CFReleaser<CFErrorRef> error;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700220 CFReleaser<SecTransformRef> signer = SecSignTransformCreate(key.get(), &error.get());
Davide Pesaventofbda9332018-09-02 16:42:33 -0400221 if (signer == nullptr) {
222 BOOST_THROW_EXCEPTION(Error("Failed to create sign transform: " + getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700223 }
224
225 // Set input
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400226 auto data = makeCFDataNoCopy(buf, size);
227 SecTransformSetAttribute(signer.get(), kSecTransformInputAttributeName, data.get(), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700228 if (error != nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400229 BOOST_THROW_EXCEPTION(Error("Failed to configure input of sign transform: " +
230 getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700231 }
232
233 // Enable use of padding
234 SecTransformSetAttribute(signer.get(), kSecPaddingKey, kSecPaddingPKCS1Key, &error.get());
235 if (error != nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400236 BOOST_THROW_EXCEPTION(Error("Failed to configure padding of sign transform: " +
237 getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700238 }
239
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400240 // Set digest type
241 SecTransformSetAttribute(signer.get(), kSecDigestTypeAttribute, getDigestAlgorithm(digestAlgo), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700242 if (error != nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400243 BOOST_THROW_EXCEPTION(Error("Failed to configure digest type of sign transform: " +
244 getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700245 }
246
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400247 // Set digest length
Davide Pesaventofbda9332018-09-02 16:42:33 -0400248 int digestSize = getDigestSize(digestAlgo);
249 CFReleaser<CFNumberRef> cfDigestSize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &digestSize);
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400250 SecTransformSetAttribute(signer.get(), kSecDigestLengthAttribute, cfDigestSize.get(), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700251 if (error != nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400252 BOOST_THROW_EXCEPTION(Error("Failed to configure digest length of sign transform: " +
253 getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700254 }
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400255
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700256 // Actually sign
257 // C-style cast is used as per Apple convention
258 CFReleaser<CFDataRef> signature = (CFDataRef)SecTransformExecute(signer.get(), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700259 if (signature == nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400260 BOOST_THROW_EXCEPTION(Error("Failed to sign data: " + getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700261 }
262
263 return make_shared<Buffer>(CFDataGetBytePtr(signature.get()), CFDataGetLength(signature.get()));
264}
265
266ConstBufferPtr
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400267BackEndOsx::decrypt(const KeyRefOsx& key, const uint8_t* cipherText, size_t cipherSize)
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700268{
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700269 CFReleaser<CFErrorRef> error;
270 CFReleaser<SecTransformRef> decryptor = SecDecryptTransformCreate(key.get(), &error.get());
Davide Pesaventofbda9332018-09-02 16:42:33 -0400271 if (decryptor == nullptr) {
272 BOOST_THROW_EXCEPTION(Error("Failed to create decrypt transform: " + getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700273 }
274
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400275 auto data = makeCFDataNoCopy(cipherText, cipherSize);
276 SecTransformSetAttribute(decryptor.get(), kSecTransformInputAttributeName, data.get(), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700277 if (error != nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400278 BOOST_THROW_EXCEPTION(Error("Failed to configure input of decrypt transform: " +
279 getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700280 }
281
282 SecTransformSetAttribute(decryptor.get(), kSecPaddingKey, kSecPaddingOAEPKey, &error.get());
283 if (error != nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400284 BOOST_THROW_EXCEPTION(Error("Failed to configure padding of decrypt transform: " +
285 getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700286 }
287
Davide Pesaventofbda9332018-09-02 16:42:33 -0400288 // C-style cast is used as per Apple convention
289 CFReleaser<CFDataRef> plainText = (CFDataRef)SecTransformExecute(decryptor.get(), &error.get());
290 if (plainText == nullptr) {
291 BOOST_THROW_EXCEPTION(Error("Failed to decrypt data: " + getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700292 }
293
Davide Pesaventofbda9332018-09-02 16:42:33 -0400294 return make_shared<Buffer>(CFDataGetBytePtr(plainText.get()), CFDataGetLength(plainText.get()));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700295}
296
297ConstBufferPtr
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400298BackEndOsx::derivePublicKey(const KeyRefOsx& key)
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700299{
300 CFReleaser<CFDataRef> exportedKey;
301 OSStatus res = SecItemExport(key.get(), // secItemOrArray
302 kSecFormatOpenSSL, // outputFormat
303 0, // flags
304 nullptr, // keyParams
305 &exportedKey.get()); // exportedData
306
307 if (res != errSecSuccess) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400308 BOOST_THROW_EXCEPTION(Error("Failed to export private key: "s + getErrorMessage(res)));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700309 }
310
311 transform::PrivateKey privateKey;
312 privateKey.loadPkcs1(CFDataGetBytePtr(exportedKey.get()), CFDataGetLength(exportedKey.get()));
313 return privateKey.derivePublicKey();
314}
315
316bool
317BackEndOsx::doHasKey(const Name& keyName) const
318{
Davide Pesaventofbda9332018-09-02 16:42:33 -0400319 return getKeyRef(keyName) != nullptr;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700320}
321
322unique_ptr<KeyHandle>
323BackEndOsx::doGetKeyHandle(const Name& keyName) const
324{
Davide Pesaventofbda9332018-09-02 16:42:33 -0400325 KeyRefOsx keyRef = getKeyRef(keyName);
326 if (keyRef == nullptr) {
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700327 return nullptr;
328 }
329
Davide Pesaventofbda9332018-09-02 16:42:33 -0400330 return make_unique<KeyHandleOsx>(keyRef.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700331}
332
333unique_ptr<KeyHandle>
334BackEndOsx::doCreateKey(const Name& identityName, const KeyParams& params)
335{
336 KeyType keyType = params.getKeyType();
337 uint32_t keySize;
338 switch (keyType) {
339 case KeyType::RSA: {
340 const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(params);
341 keySize = rsaParams.getKeySize();
342 break;
343 }
344 case KeyType::EC: {
Spyridon Mastorakis1ece2e32015-08-27 18:52:21 -0700345 const EcKeyParams& ecParams = static_cast<const EcKeyParams&>(params);
346 keySize = ecParams.getKeySize();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700347 break;
348 }
349 default: {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400350 BOOST_THROW_EXCEPTION(Tpm::Error("Failed to generate key pair: Unsupported key type"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700351 }
352 }
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400353 CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700354
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400355 auto attrDict = makeCFMutableDictionary();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700356 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, getAsymKeyType(keyType));
357 CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
358
359 KeyRefOsx publicKey, privateKey;
Davide Pesaventofbda9332018-09-02 16:42:33 -0400360 OSStatus res = SecKeyGeneratePair(attrDict.get(), &publicKey.get(), &privateKey.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700361 publicKey.retain();
362 privateKey.retain();
363
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700364 if (res != errSecSuccess) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400365 BOOST_THROW_EXCEPTION(Error("Failed to generate key pair: " + getErrorMessage(res)));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700366 }
367
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400368 unique_ptr<KeyHandle> keyHandle = make_unique<KeyHandleOsx>(privateKey.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700369 setKeyName(*keyHandle, identityName, params);
370
371 SecKeychainAttribute attrs[1]; // maximum number of attributes
Davide Pesaventofbda9332018-09-02 16:42:33 -0400372 SecKeychainAttributeList attrList = {0, attrs};
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700373 std::string keyUri = keyHandle->getKeyName().toUri();
374 {
375 attrs[attrList.count].tag = kSecKeyPrintName;
376 attrs[attrList.count].length = keyUri.size();
377 attrs[attrList.count].data = const_cast<char*>(keyUri.data());
378 attrList.count++;
379 }
380
381 SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)privateKey.get(), &attrList, 0, nullptr);
382 SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)publicKey.get(), &attrList, 0, nullptr);
383
384 return keyHandle;
385}
386
387void
388BackEndOsx::doDeleteKey(const Name& keyName)
389{
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400390 auto keyLabel = cfstring::fromStdString(keyName.toUri());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700391
Davide Pesaventofbda9332018-09-02 16:42:33 -0400392 auto query = makeCFMutableDictionary();
393 CFDictionaryAddValue(query.get(), kSecClass, kSecClassKey);
394 CFDictionaryAddValue(query.get(), kSecAttrLabel, keyLabel.get());
395 CFDictionaryAddValue(query.get(), kSecMatchLimit, kSecMatchLimitAll);
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400396
Davide Pesaventofbda9332018-09-02 16:42:33 -0400397 OSStatus res = SecItemDelete(query.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700398
Davide Pesaventofbda9332018-09-02 16:42:33 -0400399 if (res != errSecSuccess && res != errSecItemNotFound) {
400 BOOST_THROW_EXCEPTION(Error("Failed to delete key pair: " + getErrorMessage(res)));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700401 }
402}
403
404ConstBufferPtr
405BackEndOsx::doExportKey(const Name& keyName, const char* pw, size_t pwLen)
406{
Davide Pesaventofbda9332018-09-02 16:42:33 -0400407 KeyRefOsx keychainItem = getKeyRef(keyName);
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400408 if (keychainItem == nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400409 BOOST_THROW_EXCEPTION(Error("Failed to export private key: " + getErrorMessage(errSecItemNotFound)));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700410 }
411
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400412 auto passphrase = cfstring::fromBuffer(reinterpret_cast<const uint8_t*>(pw), pwLen);
Davide Pesaventofbda9332018-09-02 16:42:33 -0400413 SecItemImportExportKeyParameters keyParams;
414 std::memset(&keyParams, 0, sizeof(keyParams));
415 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700416 keyParams.passphrase = passphrase.get();
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400417
Davide Pesaventofbda9332018-09-02 16:42:33 -0400418 CFReleaser<CFDataRef> exportedKey;
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400419 OSStatus res = SecItemExport(keychainItem.get(), // secItemOrArray
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700420 kSecFormatWrappedPKCS8, // outputFormat
421 0, // flags
422 &keyParams, // keyParams
423 &exportedKey.get()); // exportedData
424
425 if (res != errSecSuccess) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400426 BOOST_THROW_EXCEPTION(Error("Failed to export private key: " + getErrorMessage(res)));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700427 }
428
429 return make_shared<Buffer>(CFDataGetBytePtr(exportedKey.get()), CFDataGetLength(exportedKey.get()));
430}
431
432void
433BackEndOsx::doImportKey(const Name& keyName, const uint8_t* buf, size_t size,
434 const char* pw, size_t pwLen)
435{
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400436 auto importedKey = makeCFDataNoCopy(buf, size);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700437
438 SecExternalFormat externalFormat = kSecFormatWrappedPKCS8;
439 SecExternalItemType externalType = kSecItemTypePrivateKey;
440
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400441 auto passphrase = cfstring::fromBuffer(reinterpret_cast<const uint8_t*>(pw), pwLen);
442 auto keyLabel = cfstring::fromStdString(keyName.toUri());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700443 CFReleaser<SecAccessRef> access;
444 SecAccessCreate(keyLabel.get(), nullptr, &access.get());
445
Davide Pesaventofbda9332018-09-02 16:42:33 -0400446 SecItemImportExportKeyParameters keyParams;
447 std::memset(&keyParams, 0, sizeof(keyParams));
448 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
449 keyParams.passphrase = passphrase.get();
450 keyParams.accessRef = access.get();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700451
452 CFReleaser<CFArrayRef> outItems;
453 OSStatus res = SecItemImport(importedKey.get(), // importedData
454 nullptr, // fileNameOrExtension
455 &externalFormat, // inputFormat
456 &externalType, // itemType
457 0, // flags
458 &keyParams, // keyParams
459 m_impl->keyChainRef, // importKeychain
460 &outItems.get()); // outItems
461
462 if (res != errSecSuccess) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400463 BOOST_THROW_EXCEPTION(Error("Failed to import private key: " + getErrorMessage(res)));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700464 }
465
466 // C-style cast is used as per Apple convention
467 SecKeychainItemRef privateKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
468 SecKeychainAttribute attrs[1]; // maximum number of attributes
Davide Pesaventofbda9332018-09-02 16:42:33 -0400469 SecKeychainAttributeList attrList = {0, attrs};
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700470 std::string keyUri = keyName.toUri();
471 {
472 attrs[attrList.count].tag = kSecKeyPrintName;
473 attrs[attrList.count].length = keyUri.size();
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400474 attrs[attrList.count].data = const_cast<char*>(keyUri.data());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700475 attrList.count++;
476 }
477
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400478 SecKeychainItemModifyAttributesAndData(privateKey, &attrList, 0, nullptr);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700479}
480
481} // namespace tpm
482} // namespace security
483} // namespace ndn