blob: daf5281feb8580dc9290fbbd6dcaaba8299e054d [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 Pesavento13fffa32018-09-30 16:21:33 -040026#include "../../encoding/buffer-stream.hpp"
Davide Pesavento5ee8ec02018-09-01 19:06:12 -040027#include "../../util/cf-string-osx.hpp"
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070028
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -040029#include <Security/Security.h>
Davide Pesaventofbda9332018-09-02 16:42:33 -040030#include <cstring>
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070031
32namespace ndn {
33namespace security {
34namespace tpm {
35
Davide Pesavento5ee8ec02018-09-01 19:06:12 -040036namespace cfstring = util::cfstring;
Junxiao Shi0b1b4672017-07-02 22:02:31 -070037using util::CFReleaser;
38
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070039class BackEndOsx::Impl
40{
41public:
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070042 SecKeychainRef keyChainRef;
Davide Pesavento5ee8ec02018-09-01 19:06:12 -040043 bool isTerminalMode = false;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070044};
45
Davide Pesavento5ee8ec02018-09-01 19:06:12 -040046static CFReleaser<CFDataRef>
47makeCFDataNoCopy(const uint8_t* buf, size_t buflen)
48{
49 return CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buf, buflen, kCFAllocatorNull);
50}
51
52static CFReleaser<CFMutableDictionaryRef>
53makeCFMutableDictionary()
54{
55 return CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
56 &kCFTypeDictionaryKeyCallBacks,
57 &kCFTypeDictionaryValueCallBacks);
58}
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070059
Davide Pesaventofbda9332018-09-02 16:42:33 -040060static std::string
61getErrorMessage(OSStatus status)
62{
63 CFReleaser<CFStringRef> msg = SecCopyErrorMessageString(status, nullptr);
64 if (msg != nullptr)
65 return cfstring::toStdString(msg.get());
66 else
67 return "<no error message>";
68}
69
70static std::string
71getFailureReason(CFErrorRef err)
72{
73 CFReleaser<CFStringRef> reason = CFErrorCopyFailureReason(err);
74 if (reason != nullptr)
75 return cfstring::toStdString(reason.get());
76 else
77 return "<unknown reason>";
78}
79
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070080static CFTypeRef
81getAsymKeyType(KeyType keyType)
82{
83 switch (keyType) {
84 case KeyType::RSA:
85 return kSecAttrKeyTypeRSA;
86 case KeyType::EC:
87 return kSecAttrKeyTypeECDSA;
88 default:
89 BOOST_THROW_EXCEPTION(Tpm::Error("Unsupported key type"));
90 }
91}
92
93static CFTypeRef
94getDigestAlgorithm(DigestAlgorithm digestAlgo)
95{
96 switch (digestAlgo) {
Davide Pesaventodef60f12017-09-17 17:26:07 -040097 case DigestAlgorithm::SHA224:
Yingdi Yu0b60e7a2015-07-16 21:05:11 -070098 case DigestAlgorithm::SHA256:
Davide Pesaventodef60f12017-09-17 17:26:07 -040099 case DigestAlgorithm::SHA384:
100 case DigestAlgorithm::SHA512:
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700101 return kSecDigestSHA2;
102 default:
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400103 return nullptr;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700104 }
105}
106
Davide Pesaventofbda9332018-09-02 16:42:33 -0400107static int
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700108getDigestSize(DigestAlgorithm digestAlgo)
109{
110 switch (digestAlgo) {
Davide Pesaventodef60f12017-09-17 17:26:07 -0400111 case DigestAlgorithm::SHA224:
112 return 224;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700113 case DigestAlgorithm::SHA256:
114 return 256;
Davide Pesaventodef60f12017-09-17 17:26:07 -0400115 case DigestAlgorithm::SHA384:
116 return 384;
117 case DigestAlgorithm::SHA512:
118 return 512;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700119 default:
120 return -1;
121 }
122}
123
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400124/**
125 * @brief Get reference to private key with name @p keyName.
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400126 */
Davide Pesaventofbda9332018-09-02 16:42:33 -0400127static KeyRefOsx
128getKeyRef(const Name& keyName)
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400129{
130 auto keyLabel = cfstring::fromStdString(keyName.toUri());
131
Davide Pesaventofbda9332018-09-02 16:42:33 -0400132 auto query = makeCFMutableDictionary();
133 CFDictionaryAddValue(query.get(), kSecClass, kSecClassKey);
134 CFDictionaryAddValue(query.get(), kSecAttrKeyClass, kSecAttrKeyClassPrivate);
135 CFDictionaryAddValue(query.get(), kSecAttrLabel, keyLabel.get());
136 CFDictionaryAddValue(query.get(), kSecReturnRef, kCFBooleanTrue);
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400137
Davide Pesaventofbda9332018-09-02 16:42:33 -0400138 KeyRefOsx keyRef;
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400139 // C-style cast is used as per Apple convention
Davide Pesaventofbda9332018-09-02 16:42:33 -0400140 OSStatus res = SecItemCopyMatching(query.get(), (CFTypeRef*)&keyRef.get());
141 keyRef.retain();
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400142
Davide Pesaventofbda9332018-09-02 16:42:33 -0400143 if (res == errSecSuccess) {
144 return keyRef;
145 }
146 else if (res == errSecItemNotFound) {
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400147 return nullptr;
148 }
Davide Pesaventofbda9332018-09-02 16:42:33 -0400149 else {
150 BOOST_THROW_EXCEPTION(BackEnd::Error("Key lookup in keychain failed: " + getErrorMessage(res)));
151 }
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400152}
153
Davide Pesavento13fffa32018-09-30 16:21:33 -0400154/**
155 * @brief Export a private key from the Keychain to @p outKey
156 */
157static void
158exportItem(const KeyRefOsx& keyRef, transform::PrivateKey& outKey)
159{
160 // use a temporary password for PKCS8 encoding
161 const char pw[] = "correct horse battery staple";
162 auto passphrase = cfstring::fromBuffer(reinterpret_cast<const uint8_t*>(pw), std::strlen(pw));
163
164 SecItemImportExportKeyParameters keyParams;
165 std::memset(&keyParams, 0, sizeof(keyParams));
166 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
167 keyParams.passphrase = passphrase.get();
168
169 CFReleaser<CFDataRef> exportedKey;
170 OSStatus res = SecItemExport(keyRef.get(), // secItemOrArray
171 kSecFormatWrappedPKCS8, // outputFormat
172 0, // flags
173 &keyParams, // keyParams
174 &exportedKey.get()); // exportedData
175
176 if (res != errSecSuccess) {
177 BOOST_THROW_EXCEPTION(BackEnd::Error("Failed to export private key: "s + getErrorMessage(res)));
178 }
179
180 outKey.loadPkcs8(CFDataGetBytePtr(exportedKey.get()), CFDataGetLength(exportedKey.get()),
181 pw, std::strlen(pw));
182}
183
Yingdi Yufe4733a2015-10-22 14:24:12 -0700184BackEndOsx::BackEndOsx(const std::string&)
Davide Pesaventodef60f12017-09-17 17:26:07 -0400185 : m_impl(make_unique<Impl>())
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700186{
187 SecKeychainSetUserInteractionAllowed(!m_impl->isTerminalMode);
188
189 OSStatus res = SecKeychainCopyDefault(&m_impl->keyChainRef);
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400190 if (res == errSecNoDefaultKeychain) {
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700191 BOOST_THROW_EXCEPTION(Error("No default keychain, create one first"));
192 }
193}
194
195BackEndOsx::~BackEndOsx() = default;
196
Yingdi Yufe4733a2015-10-22 14:24:12 -0700197const std::string&
198BackEndOsx::getScheme()
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700199{
Yingdi Yufe4733a2015-10-22 14:24:12 -0700200 static std::string scheme = "tpm-osxkeychain";
201 return scheme;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700202}
203
204bool
205BackEndOsx::isTerminalMode() const
206{
207 return m_impl->isTerminalMode;
208}
209
Yingdi Yufe4733a2015-10-22 14:24:12 -0700210void
211BackEndOsx::setTerminalMode(bool isTerminal) const
212{
213 m_impl->isTerminalMode = isTerminal;
214 SecKeychainSetUserInteractionAllowed(!isTerminal);
215}
216
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700217bool
Yingdi Yufe4733a2015-10-22 14:24:12 -0700218BackEndOsx::isTpmLocked() const
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700219{
220 SecKeychainStatus keychainStatus;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700221 OSStatus res = SecKeychainGetStatus(m_impl->keyChainRef, &keychainStatus);
222 if (res != errSecSuccess)
223 return true;
224 else
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400225 return (kSecUnlockStateStatus & keychainStatus) == 0;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700226}
227
228bool
Yingdi Yufe4733a2015-10-22 14:24:12 -0700229BackEndOsx::unlockTpm(const char* pw, size_t pwLen) const
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700230{
231 // If the default key chain is already unlocked, return immediately.
Yingdi Yufe4733a2015-10-22 14:24:12 -0700232 if (!isTpmLocked())
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700233 return true;
234
235 if (m_impl->isTerminalMode) {
236 // Use the supplied password.
Yingdi Yufe4733a2015-10-22 14:24:12 -0700237 SecKeychainUnlock(m_impl->keyChainRef, pwLen, pw, true);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700238 }
239 else {
240 // If inTerminal is not set, get the password from GUI.
241 SecKeychainUnlock(m_impl->keyChainRef, 0, nullptr, false);
242 }
243
Yingdi Yufe4733a2015-10-22 14:24:12 -0700244 return !isTpmLocked();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700245}
246
247ConstBufferPtr
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400248BackEndOsx::sign(const KeyRefOsx& key, DigestAlgorithm digestAlgo, const uint8_t* buf, size_t size)
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700249{
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700250 CFReleaser<CFErrorRef> error;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700251 CFReleaser<SecTransformRef> signer = SecSignTransformCreate(key.get(), &error.get());
Davide Pesaventofbda9332018-09-02 16:42:33 -0400252 if (signer == nullptr) {
253 BOOST_THROW_EXCEPTION(Error("Failed to create sign transform: " + getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700254 }
255
256 // Set input
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400257 auto data = makeCFDataNoCopy(buf, size);
258 SecTransformSetAttribute(signer.get(), kSecTransformInputAttributeName, data.get(), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700259 if (error != nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400260 BOOST_THROW_EXCEPTION(Error("Failed to configure input of sign transform: " +
261 getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700262 }
263
264 // Enable use of padding
265 SecTransformSetAttribute(signer.get(), kSecPaddingKey, kSecPaddingPKCS1Key, &error.get());
266 if (error != nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400267 BOOST_THROW_EXCEPTION(Error("Failed to configure padding of sign transform: " +
268 getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700269 }
270
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400271 // Set digest type
272 SecTransformSetAttribute(signer.get(), kSecDigestTypeAttribute, getDigestAlgorithm(digestAlgo), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700273 if (error != nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400274 BOOST_THROW_EXCEPTION(Error("Failed to configure digest type of sign transform: " +
275 getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700276 }
277
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400278 // Set digest length
Davide Pesaventofbda9332018-09-02 16:42:33 -0400279 int digestSize = getDigestSize(digestAlgo);
280 CFReleaser<CFNumberRef> cfDigestSize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &digestSize);
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400281 SecTransformSetAttribute(signer.get(), kSecDigestLengthAttribute, cfDigestSize.get(), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700282 if (error != nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400283 BOOST_THROW_EXCEPTION(Error("Failed to configure digest length of sign transform: " +
284 getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700285 }
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400286
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700287 // Actually sign
288 // C-style cast is used as per Apple convention
289 CFReleaser<CFDataRef> signature = (CFDataRef)SecTransformExecute(signer.get(), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700290 if (signature == nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400291 BOOST_THROW_EXCEPTION(Error("Failed to sign data: " + getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700292 }
293
294 return make_shared<Buffer>(CFDataGetBytePtr(signature.get()), CFDataGetLength(signature.get()));
295}
296
297ConstBufferPtr
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400298BackEndOsx::decrypt(const KeyRefOsx& key, const uint8_t* cipherText, size_t cipherSize)
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700299{
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700300 CFReleaser<CFErrorRef> error;
301 CFReleaser<SecTransformRef> decryptor = SecDecryptTransformCreate(key.get(), &error.get());
Davide Pesaventofbda9332018-09-02 16:42:33 -0400302 if (decryptor == nullptr) {
303 BOOST_THROW_EXCEPTION(Error("Failed to create decrypt transform: " + getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700304 }
305
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400306 auto data = makeCFDataNoCopy(cipherText, cipherSize);
307 SecTransformSetAttribute(decryptor.get(), kSecTransformInputAttributeName, data.get(), &error.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700308 if (error != nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400309 BOOST_THROW_EXCEPTION(Error("Failed to configure input of decrypt transform: " +
310 getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700311 }
312
313 SecTransformSetAttribute(decryptor.get(), kSecPaddingKey, kSecPaddingOAEPKey, &error.get());
314 if (error != nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400315 BOOST_THROW_EXCEPTION(Error("Failed to configure padding of decrypt transform: " +
316 getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700317 }
318
Davide Pesaventofbda9332018-09-02 16:42:33 -0400319 // C-style cast is used as per Apple convention
320 CFReleaser<CFDataRef> plainText = (CFDataRef)SecTransformExecute(decryptor.get(), &error.get());
321 if (plainText == nullptr) {
322 BOOST_THROW_EXCEPTION(Error("Failed to decrypt data: " + getFailureReason(error.get())));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700323 }
324
Davide Pesaventofbda9332018-09-02 16:42:33 -0400325 return make_shared<Buffer>(CFDataGetBytePtr(plainText.get()), CFDataGetLength(plainText.get()));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700326}
327
328ConstBufferPtr
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400329BackEndOsx::derivePublicKey(const KeyRefOsx& key)
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700330{
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700331 transform::PrivateKey privateKey;
Davide Pesavento13fffa32018-09-30 16:21:33 -0400332 exportItem(key, privateKey);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700333 return privateKey.derivePublicKey();
334}
335
336bool
337BackEndOsx::doHasKey(const Name& keyName) const
338{
Davide Pesaventofbda9332018-09-02 16:42:33 -0400339 return getKeyRef(keyName) != nullptr;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700340}
341
342unique_ptr<KeyHandle>
343BackEndOsx::doGetKeyHandle(const Name& keyName) const
344{
Davide Pesaventofbda9332018-09-02 16:42:33 -0400345 KeyRefOsx keyRef = getKeyRef(keyName);
346 if (keyRef == nullptr) {
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700347 return nullptr;
348 }
349
Davide Pesaventofbda9332018-09-02 16:42:33 -0400350 return make_unique<KeyHandleOsx>(keyRef.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700351}
352
353unique_ptr<KeyHandle>
354BackEndOsx::doCreateKey(const Name& identityName, const KeyParams& params)
355{
356 KeyType keyType = params.getKeyType();
357 uint32_t keySize;
358 switch (keyType) {
359 case KeyType::RSA: {
360 const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(params);
361 keySize = rsaParams.getKeySize();
362 break;
363 }
364 case KeyType::EC: {
Spyridon Mastorakis1ece2e32015-08-27 18:52:21 -0700365 const EcKeyParams& ecParams = static_cast<const EcKeyParams&>(params);
366 keySize = ecParams.getKeySize();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700367 break;
368 }
369 default: {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400370 BOOST_THROW_EXCEPTION(Tpm::Error("Failed to generate key pair: Unsupported key type"));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700371 }
372 }
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400373 CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700374
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400375 auto attrDict = makeCFMutableDictionary();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700376 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, getAsymKeyType(keyType));
377 CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
378
379 KeyRefOsx publicKey, privateKey;
Davide Pesaventofbda9332018-09-02 16:42:33 -0400380 OSStatus res = SecKeyGeneratePair(attrDict.get(), &publicKey.get(), &privateKey.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700381 publicKey.retain();
382 privateKey.retain();
383
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700384 if (res != errSecSuccess) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400385 BOOST_THROW_EXCEPTION(Error("Failed to generate key pair: " + getErrorMessage(res)));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700386 }
387
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400388 unique_ptr<KeyHandle> keyHandle = make_unique<KeyHandleOsx>(privateKey.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700389 setKeyName(*keyHandle, identityName, params);
390
391 SecKeychainAttribute attrs[1]; // maximum number of attributes
Davide Pesaventofbda9332018-09-02 16:42:33 -0400392 SecKeychainAttributeList attrList = {0, attrs};
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700393 std::string keyUri = keyHandle->getKeyName().toUri();
394 {
395 attrs[attrList.count].tag = kSecKeyPrintName;
396 attrs[attrList.count].length = keyUri.size();
397 attrs[attrList.count].data = const_cast<char*>(keyUri.data());
398 attrList.count++;
399 }
400
401 SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)privateKey.get(), &attrList, 0, nullptr);
402 SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)publicKey.get(), &attrList, 0, nullptr);
403
404 return keyHandle;
405}
406
407void
408BackEndOsx::doDeleteKey(const Name& keyName)
409{
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400410 auto keyLabel = cfstring::fromStdString(keyName.toUri());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700411
Davide Pesaventofbda9332018-09-02 16:42:33 -0400412 auto query = makeCFMutableDictionary();
413 CFDictionaryAddValue(query.get(), kSecClass, kSecClassKey);
414 CFDictionaryAddValue(query.get(), kSecAttrLabel, keyLabel.get());
415 CFDictionaryAddValue(query.get(), kSecMatchLimit, kSecMatchLimitAll);
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400416
Davide Pesaventofbda9332018-09-02 16:42:33 -0400417 OSStatus res = SecItemDelete(query.get());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700418
Davide Pesaventofbda9332018-09-02 16:42:33 -0400419 if (res != errSecSuccess && res != errSecItemNotFound) {
420 BOOST_THROW_EXCEPTION(Error("Failed to delete key pair: " + getErrorMessage(res)));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700421 }
422}
423
424ConstBufferPtr
425BackEndOsx::doExportKey(const Name& keyName, const char* pw, size_t pwLen)
426{
Davide Pesaventofbda9332018-09-02 16:42:33 -0400427 KeyRefOsx keychainItem = getKeyRef(keyName);
Davide Pesavento5ee8ec02018-09-01 19:06:12 -0400428 if (keychainItem == nullptr) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400429 BOOST_THROW_EXCEPTION(Error("Failed to export private key: " + getErrorMessage(errSecItemNotFound)));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700430 }
431
Davide Pesavento13fffa32018-09-30 16:21:33 -0400432 transform::PrivateKey exportedKey;
433 OBufferStream pkcs8;
434 try {
435 exportItem(keychainItem, exportedKey);
436 exportedKey.savePkcs8(pkcs8, pw, pwLen);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700437 }
Davide Pesavento13fffa32018-09-30 16:21:33 -0400438 catch (const transform::PrivateKey::Error& e) {
439 BOOST_THROW_EXCEPTION(Error("Failed to export private key: "s + e.what()));
440 }
441 return pkcs8.buf();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700442}
443
444void
445BackEndOsx::doImportKey(const Name& keyName, const uint8_t* buf, size_t size,
446 const char* pw, size_t pwLen)
447{
Davide Pesavento13fffa32018-09-30 16:21:33 -0400448 transform::PrivateKey privKey;
449 OBufferStream pkcs1;
450 try {
451 // do the PKCS8 decoding ourselves, see bug #4450
452 privKey.loadPkcs8(buf, size, pw, pwLen);
453 privKey.savePkcs1(pkcs1);
454 }
455 catch (const transform::PrivateKey::Error& e) {
456 BOOST_THROW_EXCEPTION(Error("Failed to import private key: "s + e.what()));
457 }
458 auto keyToImport = makeCFDataNoCopy(pkcs1.buf()->data(), pkcs1.buf()->size());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700459
Davide Pesavento13fffa32018-09-30 16:21:33 -0400460 SecExternalFormat externalFormat = kSecFormatOpenSSL;
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700461 SecExternalItemType externalType = kSecItemTypePrivateKey;
462
Davide Pesavento13fffa32018-09-30 16:21:33 -0400463 auto keyUri = keyName.toUri();
464 auto keyLabel = cfstring::fromStdString(keyUri);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700465 CFReleaser<SecAccessRef> access;
Davide Pesavento13fffa32018-09-30 16:21:33 -0400466 OSStatus res = SecAccessCreate(keyLabel.get(), // descriptor
467 nullptr, // trustedlist (null == trust only the calling app)
468 &access.get()); // accessRef
469
470 if (res != errSecSuccess) {
471 BOOST_THROW_EXCEPTION(Error("Failed to import private key: " + getErrorMessage(res)));
472 }
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700473
Davide Pesaventofbda9332018-09-02 16:42:33 -0400474 SecItemImportExportKeyParameters keyParams;
475 std::memset(&keyParams, 0, sizeof(keyParams));
476 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
Davide Pesaventofbda9332018-09-02 16:42:33 -0400477 keyParams.accessRef = access.get();
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700478
479 CFReleaser<CFArrayRef> outItems;
Davide Pesavento13fffa32018-09-30 16:21:33 -0400480 res = SecItemImport(keyToImport.get(), // importedData
481 nullptr, // fileNameOrExtension
482 &externalFormat, // inputFormat
483 &externalType, // itemType
484 0, // flags
485 &keyParams, // keyParams
486 m_impl->keyChainRef, // importKeychain
487 &outItems.get()); // outItems
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700488
489 if (res != errSecSuccess) {
Davide Pesaventofbda9332018-09-02 16:42:33 -0400490 BOOST_THROW_EXCEPTION(Error("Failed to import private key: " + getErrorMessage(res)));
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700491 }
492
493 // C-style cast is used as per Apple convention
Davide Pesavento13fffa32018-09-30 16:21:33 -0400494 SecKeychainItemRef keychainItem = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700495 SecKeychainAttribute attrs[1]; // maximum number of attributes
Davide Pesaventofbda9332018-09-02 16:42:33 -0400496 SecKeychainAttributeList attrList = {0, attrs};
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700497 {
498 attrs[attrList.count].tag = kSecKeyPrintName;
499 attrs[attrList.count].length = keyUri.size();
Davide Pesaventoff3bf2c2017-10-05 00:14:27 -0400500 attrs[attrList.count].data = const_cast<char*>(keyUri.data());
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700501 attrList.count++;
502 }
Davide Pesavento13fffa32018-09-30 16:21:33 -0400503 SecKeychainItemModifyAttributesAndData(keychainItem, &attrList, 0, nullptr);
Yingdi Yu0b60e7a2015-07-16 21:05:11 -0700504}
505
506} // namespace tpm
507} // namespace security
508} // namespace ndn