blob: ada0b81467ad3d46b489c3e8321288eeb49f6d25 [file] [log] [blame]
Jeff Thompson2747dc02013-10-04 19:11:34 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/**
3 * Copyright (C) 2013 Regents of the University of California.
4 * @author: Yingdi Yu <yingdi@cs.ucla.edu>
5 * See COPYING for copyright and distribution information.
6 */
7
Alexander Afanasyeve2dcdfd2014-02-07 15:53:28 -08008#include "common.hpp"
9
Alexander Afanasyev19508852014-01-29 01:01:51 -080010#include "sec-tpm-osx.hpp"
11
12#include "security/public-key.hpp"
13#include "util/logging.hpp"
Junxiao Shi482ccc52014-03-31 13:05:24 -070014#include "cryptopp.hpp"
Jeff Thompson2747dc02013-10-04 19:11:34 -070015
Yingdi Yu2b2b4792014-02-04 16:27:07 -080016#include <pwd.h>
17#include <unistd.h>
18#include <stdlib.h>
19#include <string.h>
Jeff Thompson2747dc02013-10-04 19:11:34 -070020
Alexander Afanasyev04b22a92014-01-05 22:40:17 -080021#include <CoreFoundation/CoreFoundation.h>
22#include <Security/Security.h>
Yingdi Yu4b752752014-02-18 12:24:03 -080023#include <Security/SecRandom.h>
Alexander Afanasyev04b22a92014-01-05 22:40:17 -080024#include <CoreServices/CoreServices.h>
Jeff Thompson2747dc02013-10-04 19:11:34 -070025
26using namespace std;
Jeff Thompson2747dc02013-10-04 19:11:34 -070027
Yingdi Yu21157162014-02-28 13:02:34 -080028INIT_LOGGER("ndn.SecTpmOsx");
Jeff Thompson2747dc02013-10-04 19:11:34 -070029
Yingdi Yufc40d872014-02-18 12:56:04 -080030namespace ndn {
31
Yingdi Yu2b2b4792014-02-04 16:27:07 -080032class SecTpmOsx::Impl {
33public:
34 Impl()
Yingdi Yube4150e2014-02-18 13:02:46 -080035 : m_passwordSet(false)
36 , m_inTerminal(false)
Yingdi Yu2b2b4792014-02-04 16:27:07 -080037 {}
Alexander Afanasyev04b22a92014-01-05 22:40:17 -080038
Yingdi Yu2b2b4792014-02-04 16:27:07 -080039 /**
40 * @brief Convert NDN name of a key to internal name of the key.
41 *
Yingdi Yufc40d872014-02-18 12:56:04 -080042 * @param keyName
43 * @param keyClass
Yingdi Yu2b2b4792014-02-04 16:27:07 -080044 * @return the internal key name
45 */
46 std::string
47 toInternalKeyName(const Name & keyName, KeyClass keyClass);
Alexander Afanasyev04b22a92014-01-05 22:40:17 -080048
Yingdi Yu2b2b4792014-02-04 16:27:07 -080049 /**
50 * @brief Get key.
Yingdi Yufc40d872014-02-18 12:56:04 -080051 *
52 * @param keyName
53 * @param keyClass
Yingdi Yu2b2b4792014-02-04 16:27:07 -080054 * @returns pointer to the key
55 */
56 SecKeychainItemRef
57 getKey(const Name & keyName, KeyClass keyClass);
Alexander Afanasyev04b22a92014-01-05 22:40:17 -080058
Yingdi Yu2b2b4792014-02-04 16:27:07 -080059 /**
Yingdi Yufc40d872014-02-18 12:56:04 -080060 * @brief Convert keyType to MAC OS symmetric key key type
61 *
Yingdi Yu2b2b4792014-02-04 16:27:07 -080062 * @param keyType
63 * @returns MAC OS key type
64 */
Yingdi Yu87581582014-01-14 14:28:39 -080065 const CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -080066 getSymKeyType(KeyType keyType);
67
68 /**
Yingdi Yufc40d872014-02-18 12:56:04 -080069 * @brief Convert keyType to MAC OS asymmetirc key type
70 *
Yingdi Yu2b2b4792014-02-04 16:27:07 -080071 * @param keyType
72 * @returns MAC OS key type
73 */
Yingdi Yu87581582014-01-14 14:28:39 -080074 const CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -080075 getAsymKeyType(KeyType keyType);
76
77 /**
Yingdi Yufc40d872014-02-18 12:56:04 -080078 * @brief Convert keyClass to MAC OS key class
79 *
Yingdi Yu2b2b4792014-02-04 16:27:07 -080080 * @param keyClass
81 * @returns MAC OS key class
82 */
Yingdi Yu87581582014-01-14 14:28:39 -080083 const CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -080084 getKeyClass(KeyClass keyClass);
85
86 /**
Yingdi Yufc40d872014-02-18 12:56:04 -080087 * @brief Convert digestAlgo to MAC OS algorithm id
88 *
Yingdi Yu2b2b4792014-02-04 16:27:07 -080089 * @param digestAlgo
90 * @returns MAC OS algorithm id
91 */
Yingdi Yu87581582014-01-14 14:28:39 -080092 const CFStringRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -080093 getDigestAlgorithm(DigestAlgorithm digestAlgo);
94
95 /**
Yingdi Yufc40d872014-02-18 12:56:04 -080096 * @brief Get the digest size of the corresponding algorithm
97 *
98 * @param digestAlgo
Yingdi Yu2b2b4792014-02-04 16:27:07 -080099 * @return digest size
100 */
101 long
102 getDigestSize(DigestAlgorithm digestAlgo);
103
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800104 ///////////////////////////////////////////////
105 // everything here is public, including data //
106 ///////////////////////////////////////////////
107public:
108 SecKeychainRef m_keyChainRef;
Yingdi Yube4150e2014-02-18 13:02:46 -0800109 bool m_passwordSet;
110 string m_password;
111 bool m_inTerminal;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800112};
113
114
115SecTpmOsx::SecTpmOsx()
116 : m_impl(new Impl)
117{
Yingdi Yube4150e2014-02-18 13:02:46 -0800118 if(m_impl->m_inTerminal)
119 SecKeychainSetUserInteractionAllowed (false);
120 else
121 SecKeychainSetUserInteractionAllowed (true);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800122
Yingdi Yube4150e2014-02-18 13:02:46 -0800123 OSStatus res = SecKeychainCopyDefault(&m_impl->m_keyChainRef);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800124
125 if (res == errSecNoDefaultKeychain) //If no default key chain, create one.
Yingdi Yube4150e2014-02-18 13:02:46 -0800126 throw Error("No default keychain, create one first!");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800127}
128
129SecTpmOsx::~SecTpmOsx(){
130 //TODO: implement
131}
132
Yingdi Yube4150e2014-02-18 13:02:46 -0800133void
134SecTpmOsx::setTpmPassword(const uint8_t* password, size_t passwordLength)
135{
136 m_impl->m_passwordSet = true;
137 memset(const_cast<char*>(m_impl->m_password.c_str()), 0, m_impl->m_password.size());
138 m_impl->m_password.clear();
139 m_impl->m_password.append(reinterpret_cast<const char*>(password), passwordLength);
140}
141
142void
143SecTpmOsx::resetTpmPassword()
144{
145 m_impl->m_passwordSet = false;
146 memset(const_cast<char*>(m_impl->m_password.c_str()), 0, m_impl->m_password.size());
147 m_impl->m_password.clear();
148}
149
150void
151SecTpmOsx::setInTerminal(bool inTerminal)
152{
153 m_impl->m_inTerminal = inTerminal;
154 if(inTerminal)
155 SecKeychainSetUserInteractionAllowed (false);
156 else
157 SecKeychainSetUserInteractionAllowed (true);
158}
159
160bool
161SecTpmOsx::getInTerminal()
162{
163 return m_impl->m_inTerminal;
164}
165
166bool
167SecTpmOsx::locked()
168{
169 SecKeychainStatus keychainStatus;
170
171 OSStatus res = SecKeychainGetStatus(m_impl->m_keyChainRef, &keychainStatus);
172 if(res != errSecSuccess)
173 return true;
174 else
175 return ((kSecUnlockStateStatus & keychainStatus) == 0);
176}
177
Yingdi Yu2e57a582014-02-20 23:34:43 -0800178bool
Yingdi Yube4150e2014-02-18 13:02:46 -0800179SecTpmOsx::unlockTpm(const char* password, size_t passwordLength, bool usePassword)
180{
181 OSStatus res;
182
183 // If the default key chain is already unlocked, return immediately.
184 if(!locked())
Yingdi Yu2e57a582014-02-20 23:34:43 -0800185 return true;
Yingdi Yube4150e2014-02-18 13:02:46 -0800186
187 // If the default key chain is locked, unlock the key chain.
188 if(usePassword)
189 {
190 // Use the supplied password.
191 res = SecKeychainUnlock(m_impl->m_keyChainRef,
192 passwordLength,
193 password,
194 true);
195 }
196 else if(m_impl->m_passwordSet)
197 {
198 // If no password supplied, then use the configured password if exists.
199 SecKeychainUnlock(m_impl->m_keyChainRef,
200 m_impl->m_password.size(),
201 m_impl->m_password.c_str(),
202 true);
203 }
204 else if(m_impl->m_inTerminal)
205 {
206 // If no configured password, get password from terminal if inTerminal set.
207 bool locked = true;
208 const char* fmt = "Password to unlock the default keychain: ";
209 int count = 0;
210
211 while(locked)
212 {
213 if(count > 2)
214 break;
215
216 char* getPassword = NULL;
217 getPassword = getpass(fmt);
218 count++;
219
220 if (!getPassword)
221 continue;
222
223 res = SecKeychainUnlock(m_impl->m_keyChainRef,
224 strlen(getPassword),
225 getPassword,
226 true);
227
228 memset(getPassword, 0, strlen(getPassword));
229
230 if(res == errSecSuccess)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800231 break;
Yingdi Yube4150e2014-02-18 13:02:46 -0800232 }
233 }
234 else
235 {
236 // If inTerminal is not set, get the password from GUI.
237 SecKeychainUnlock(m_impl->m_keyChainRef, 0, 0, false);
238 }
Yingdi Yu2e57a582014-02-20 23:34:43 -0800239
240 return !locked();
Yingdi Yube4150e2014-02-18 13:02:46 -0800241}
242
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800243void
Yingdi Yube4150e2014-02-18 13:02:46 -0800244SecTpmOsx::generateKeyPairInTpmInternal(const Name & keyName, KeyType keyType, int keySize, bool retry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800245{
246
247 if(doesKeyExistInTpm(keyName, KEY_CLASS_PUBLIC)){
248 _LOG_DEBUG("keyName has existed");
249 throw Error("keyName has existed");
250 }
251
252 string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_PUBLIC);
253
254 SecKeyRef publicKey, privateKey;
255
256 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
257 keyNameUri.c_str(),
258 kCFStringEncodingUTF8);
259
260 CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL,
261 3,
262 &kCFTypeDictionaryKeyCallBacks,
263 NULL);
264
265 CFDictionaryAddValue(attrDict, kSecAttrKeyType, m_impl->getAsymKeyType(keyType));
266 CFDictionaryAddValue(attrDict, kSecAttrKeySizeInBits, CFNumberCreate(NULL, kCFNumberIntType, &keySize));
267 CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
268
269 OSStatus res = SecKeyGeneratePair((CFDictionaryRef)attrDict, &publicKey, &privateKey);
270
Yingdi Yube4150e2014-02-18 13:02:46 -0800271 if (res == errSecSuccess)
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800272 {
273 CFRelease(publicKey);
274 CFRelease(privateKey);
275 return;
276 }
Yingdi Yube4150e2014-02-18 13:02:46 -0800277
278 if (res == errSecAuthFailed && !retry)
279 {
Yingdi Yu2e57a582014-02-20 23:34:43 -0800280 if(unlockTpm(0, 0, false))
281 generateKeyPairInTpmInternal(keyName, keyType, keySize, true);
282 else
283 throw Error("Fail to unlock the keychain");
Yingdi Yube4150e2014-02-18 13:02:46 -0800284 }
285 else
286 {
287 _LOG_DEBUG("Fail to create a key pair: " << res);
288 throw Error("Fail to create a key pair");
289 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800290}
291
292void
Yingdi Yube4150e2014-02-18 13:02:46 -0800293SecTpmOsx::deleteKeyPairInTpmInternal(const Name &keyName, bool retry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800294{
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800295 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800296 keyName.toUri().c_str(),
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800297 kCFStringEncodingUTF8);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800298
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800299 CFMutableDictionaryRef searchDict =
300 CFDictionaryCreateMutable(NULL, 5, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800301
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800302 CFDictionaryAddValue(searchDict, kSecClass, kSecClassKey);
303 CFDictionaryAddValue(searchDict, kSecAttrLabel, keyLabel);
304 CFDictionaryAddValue(searchDict, kSecMatchLimit, kSecMatchLimitAll);
Yingdi Yube4150e2014-02-18 13:02:46 -0800305 OSStatus res = SecItemDelete(searchDict);
306
307 if (res == errSecSuccess)
308 return;
309
310 if (res == errSecAuthFailed && !retry)
311 {
Yingdi Yu2e57a582014-02-20 23:34:43 -0800312 if(unlockTpm(0, 0, false))
313 deleteKeyPairInTpmInternal(keyName, true);
Yingdi Yube4150e2014-02-18 13:02:46 -0800314 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800315}
316
317void
318SecTpmOsx::generateSymmetricKeyInTpm(const Name & keyName, KeyType keyType, int keySize)
319{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800320 throw Error("SecTpmOsx::generateSymmetricKeyInTpm is not supported");
321 // if(doesKeyExistInTpm(keyName, KEY_CLASS_SYMMETRIC))
322 // throw Error("keyName has existed!");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800323
Yingdi Yu2e57a582014-02-20 23:34:43 -0800324 // string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_SYMMETRIC);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800325
Yingdi Yu2e57a582014-02-20 23:34:43 -0800326 // CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
327 // 0,
328 // &kCFTypeDictionaryKeyCallBacks,
329 // &kCFTypeDictionaryValueCallBacks);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800330
Yingdi Yu2e57a582014-02-20 23:34:43 -0800331 // CFStringRef keyLabel = CFStringCreateWithCString(NULL,
332 // keyNameUri.c_str(),
333 // kCFStringEncodingUTF8);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800334
Yingdi Yu2e57a582014-02-20 23:34:43 -0800335 // CFDictionaryAddValue(attrDict, kSecAttrKeyType, m_impl->getSymKeyType(keyType));
336 // CFDictionaryAddValue(attrDict, kSecAttrKeySizeInBits, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &keySize));
337 // CFDictionaryAddValue(attrDict, kSecAttrIsPermanent, kCFBooleanTrue);
338 // CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800339
Yingdi Yu2e57a582014-02-20 23:34:43 -0800340 // CFErrorRef error = NULL;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800341
Yingdi Yu2e57a582014-02-20 23:34:43 -0800342 // SecKeyRef symmetricKey = SecKeyGenerateSymmetric(attrDict, &error);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800343
Yingdi Yu2e57a582014-02-20 23:34:43 -0800344 // if (error)
345 // throw Error("Fail to create a symmetric key");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800346}
347
Yingdi Yu2e57a582014-02-20 23:34:43 -0800348shared_ptr<PublicKey>
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800349SecTpmOsx::getPublicKeyFromTpm(const Name & keyName)
350{
351 _LOG_TRACE("OSXPrivateKeyStorage::getPublickey");
352
353 SecKeychainItemRef publicKey = m_impl->getKey(keyName, KEY_CLASS_PUBLIC);
354
355 CFDataRef exportedKey;
356
357 OSStatus res = SecItemExport(publicKey,
358 kSecFormatOpenSSL,
359 0,
360 NULL,
361 &exportedKey);
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800362 if (res != errSecSuccess)
363 {
364 throw Error("Cannot export requested public key from OSX Keychain");
365 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800366
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800367 shared_ptr<PublicKey> key = make_shared<PublicKey>(CFDataGetBytePtr(exportedKey), CFDataGetLength(exportedKey));
368 CFRelease(exportedKey);
369 return key;
370}
371
372ConstBufferPtr
Yingdi Yube4150e2014-02-18 13:02:46 -0800373SecTpmOsx::exportPrivateKeyPkcs1FromTpmInternal(const Name& keyName, bool retry)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800374{
375 using namespace CryptoPP;
376
377 SecKeychainItemRef privateKey = m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
378 CFDataRef exportedKey;
379 OSStatus res = SecItemExport(privateKey,
380 kSecFormatOpenSSL,
381 0,
382 NULL,
383 &exportedKey);
384
385 if(res != errSecSuccess)
386 {
Yingdi Yube4150e2014-02-18 13:02:46 -0800387 if(res == errSecAuthFailed && !retry)
388 {
Yingdi Yu2e57a582014-02-20 23:34:43 -0800389 if(unlockTpm(0, 0, false))
390 return exportPrivateKeyPkcs1FromTpmInternal(keyName, true);
391 else
392 return shared_ptr<Buffer>();
Yingdi Yube4150e2014-02-18 13:02:46 -0800393 }
394 else
395 return shared_ptr<Buffer>();
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800396 }
397
398 OBufferStream pkcs1Os;
399 FileSink sink(pkcs1Os);
400
401 uint32_t version = 0;
402 OID algorithm("1.2.840.113549.1.1.1");
403 SecByteBlock rawKeyBits;
404 // PrivateKeyInfo ::= SEQUENCE {
405 // version INTEGER,
406 // privateKeyAlgorithm SEQUENCE,
407 // privateKey OCTECT STRING}
408 DERSequenceEncoder privateKeyInfo(sink);
409 {
410 DEREncodeUnsigned<uint32_t>(privateKeyInfo, version, INTEGER);
411 DERSequenceEncoder privateKeyAlgorithm(privateKeyInfo);
412 {
413 algorithm.encode(privateKeyAlgorithm);
414 DEREncodeNull(privateKeyAlgorithm);
415 }
416 privateKeyAlgorithm.MessageEnd();
417 DEREncodeOctetString(privateKeyInfo, CFDataGetBytePtr(exportedKey), CFDataGetLength(exportedKey));
418 }
419 privateKeyInfo.MessageEnd();
420
421 CFRelease(exportedKey);
422 return pkcs1Os.buf();
423}
424
425bool
Yingdi Yube4150e2014-02-18 13:02:46 -0800426SecTpmOsx::importPrivateKeyPkcs1IntoTpmInternal(const Name& keyName, const uint8_t* buf, size_t size, bool retry)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800427{
428 using namespace CryptoPP;
429
430 StringSource privateKeySource(buf, size, true);
431 uint32_t tmpNum;
432 OID tmpOID;
433 SecByteBlock rawKeyBits;
434 // PrivateKeyInfo ::= SEQUENCE {
435 // INTEGER,
436 // SEQUENCE,
437 // OCTECT STRING}
438 BERSequenceDecoder privateKeyInfo(privateKeySource);
439 {
440 BERDecodeUnsigned<uint32_t>(privateKeyInfo, tmpNum, INTEGER);
441 BERSequenceDecoder sequenceDecoder(privateKeyInfo);
442 {
443 tmpOID.decode(sequenceDecoder);
444 BERDecodeNull(sequenceDecoder);
445 }
446 BERDecodeOctetString(privateKeyInfo, rawKeyBits);
447 }
448 privateKeyInfo.MessageEnd();
449
450 CFDataRef importedKey = CFDataCreateWithBytesNoCopy(NULL,
451 rawKeyBits.BytePtr(),
452 rawKeyBits.size(),
453 kCFAllocatorNull);
454
455 SecExternalFormat externalFormat = kSecFormatOpenSSL;
456 SecExternalItemType externalType = kSecItemTypePrivateKey;
457 SecKeyImportExportParameters keyParams;
458 memset(&keyParams, 0, sizeof(keyParams));
459 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
460 keyParams.keyAttributes = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT;
461 SecAccessRef access;
462 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
463 keyName.toUri().c_str(),
464 kCFStringEncodingUTF8);
465 SecAccessCreate(keyLabel, NULL, &access);
466 keyParams.accessRef = access;
467 CFArrayRef outItems;
468
Junxiao Shi482ccc52014-03-31 13:05:24 -0700469#pragma clang diagnostic push
470#pragma clang diagnostic ignored "-Wdeprecated-declarations"
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800471 OSStatus res = SecKeychainItemImport (importedKey,
472 NULL,
473 &externalFormat,
474 &externalType,
475 0,
476 &keyParams,
477 m_impl->m_keyChainRef,
478 &outItems);
Junxiao Shi482ccc52014-03-31 13:05:24 -0700479#pragma clang diagnostic pop
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800480
481 if(res != errSecSuccess)
482 {
Yingdi Yube4150e2014-02-18 13:02:46 -0800483 if(res == errSecAuthFailed && !retry)
484 {
Yingdi Yu2e57a582014-02-20 23:34:43 -0800485 if(unlockTpm(0, 0, false))
486 return importPrivateKeyPkcs1IntoTpmInternal(keyName, buf, size, true);
487 else
488 return false;
Yingdi Yube4150e2014-02-18 13:02:46 -0800489 }
490 else
491 return false;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800492 }
493
494 SecKeychainItemRef privateKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems, 0);
495 SecKeychainAttribute attrs[1]; // maximum number of attributes
496 SecKeychainAttributeList attrList = { 0, attrs };
497 string keyUri = keyName.toUri();
498 {
499 attrs[attrList.count].tag = kSecKeyPrintName;
500 attrs[attrList.count].length = keyUri.size();
501 attrs[attrList.count].data = (void *)keyUri.c_str();
502 attrList.count++;
503 }
504
505 res = SecKeychainItemModifyAttributesAndData(privateKey,
506 &attrList,
507 0,
508 NULL);
509
510 if(res != errSecSuccess)
511 {
512 return false;
513 }
514
515 CFRelease(importedKey);
516 return true;
517}
518
519bool
520SecTpmOsx::importPublicKeyPkcs1IntoTpm(const Name& keyName, const uint8_t* buf, size_t size)
521{
522 CFDataRef importedKey = CFDataCreateWithBytesNoCopy(NULL,
523 buf,
524 size,
525 kCFAllocatorNull);
526
527 SecExternalFormat externalFormat = kSecFormatOpenSSL;
528 SecExternalItemType externalType = kSecItemTypePublicKey;
529 CFArrayRef outItems;
530
531 OSStatus res = SecItemImport (importedKey,
532 NULL,
533 &externalFormat,
534 &externalType,
535 0,
536 NULL,
537 m_impl->m_keyChainRef,
538 &outItems);
539
540 if(res != errSecSuccess)
541 return false;
542
543 SecKeychainItemRef publicKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems, 0);
544 SecKeychainAttribute attrs[1]; // maximum number of attributes
545 SecKeychainAttributeList attrList = { 0, attrs };
546 string keyUri = keyName.toUri();
547 {
548 attrs[attrList.count].tag = kSecKeyPrintName;
549 attrs[attrList.count].length = keyUri.size();
550 attrs[attrList.count].data = (void *)keyUri.c_str();
551 attrList.count++;
552 }
553
554 res = SecKeychainItemModifyAttributesAndData(publicKey,
555 &attrList,
556 0,
557 NULL);
558
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800559 if(res != errSecSuccess)
560 return false;
561
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800562 CFRelease(importedKey);
563 return true;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800564}
565
566Block
Yingdi Yube4150e2014-02-18 13:02:46 -0800567SecTpmOsx::signInTpmInternal(const uint8_t *data, size_t dataLength, const Name& keyName, DigestAlgorithm digestAlgorithm, bool retry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800568{
569 _LOG_TRACE("OSXPrivateKeyStorage::Sign");
570
571 CFDataRef dataRef = CFDataCreateWithBytesNoCopy(NULL,
572 data,
573 dataLength,
Yingdi Yu2e57a582014-02-20 23:34:43 -0800574 kCFAllocatorNull);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800575
576 SecKeyRef privateKey = (SecKeyRef)m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
577
578 CFErrorRef error;
579 SecTransformRef signer = SecSignTransformCreate((SecKeyRef)privateKey, &error);
580 if (error) throw Error("Fail to create signer");
581
582 // Set input
583 Boolean set_res = SecTransformSetAttribute(signer,
584 kSecTransformInputAttributeName,
585 dataRef,
586 &error);
587 if (error) throw Error("Fail to configure input of signer");
588
589 // Enable use of padding
Yingdi Yu2e57a582014-02-20 23:34:43 -0800590 SecTransformSetAttribute(signer,
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800591 kSecPaddingKey,
592 kSecPaddingPKCS1Key,
593 &error);
594 if (error) throw Error("Fail to configure digest algorithm of signer");
595
596 // Set padding type
597 set_res = SecTransformSetAttribute(signer,
598 kSecDigestTypeAttribute,
599 m_impl->getDigestAlgorithm(digestAlgorithm),
600 &error);
601 if (error) throw Error("Fail to configure digest algorithm of signer");
602
603 // Set padding attribute
604 long digestSize = m_impl->getDigestSize(digestAlgorithm);
605 set_res = SecTransformSetAttribute(signer,
606 kSecDigestLengthAttribute,
607 CFNumberCreate(NULL, kCFNumberLongType, &digestSize),
608 &error);
609 if (error) throw Error("Fail to configure digest size of signer");
610
611 // Actually sign
612 CFDataRef signature = (CFDataRef) SecTransformExecute(signer, &error);
Yingdi Yube4150e2014-02-18 13:02:46 -0800613 if (error)
614 {
615 if(!retry)
616 {
Yingdi Yu2e57a582014-02-20 23:34:43 -0800617 if(unlockTpm(0, 0, false))
618 return signInTpmInternal(data, dataLength, keyName, digestAlgorithm, true);
619 else
620 throw Error("Fail to unlock the keychain");
Yingdi Yube4150e2014-02-18 13:02:46 -0800621 }
622 else
623 {
624 CFShow(error);
625 throw Error("Fail to sign data");
626 }
627 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800628
629 if (!signature) throw Error("Signature is NULL!\n");
630
631 return Block(Tlv::SignatureValue,
Yingdi Yu2e57a582014-02-20 23:34:43 -0800632 make_shared<Buffer>(CFDataGetBytePtr(signature), CFDataGetLength(signature)));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800633}
634
635ConstBufferPtr
Yingdi Yufc40d872014-02-18 12:56:04 -0800636SecTpmOsx::decryptInTpm(const uint8_t* data, size_t dataLength, const Name & keyName, bool sym)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800637{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800638 throw Error("SecTpmOsx::decryptInTpm is not supported");
639 // _LOG_TRACE("OSXPrivateKeyStorage::Decrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800640
Yingdi Yu2e57a582014-02-20 23:34:43 -0800641 // KeyClass keyClass;
642 // if(sym)
643 // keyClass = KEY_CLASS_SYMMETRIC;
644 // else
645 // keyClass = KEY_CLASS_PRIVATE;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800646
Yingdi Yu2e57a582014-02-20 23:34:43 -0800647 // CFDataRef dataRef = CFDataCreate(NULL,
648 // reinterpret_cast<const unsigned char*>(data),
649 // dataLength
650 // );
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800651
Yingdi Yu2e57a582014-02-20 23:34:43 -0800652 // // _LOG_DEBUG("CreateData");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800653
Yingdi Yu2e57a582014-02-20 23:34:43 -0800654 // SecKeyRef decryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800655
Yingdi Yu2e57a582014-02-20 23:34:43 -0800656 // // _LOG_DEBUG("GetKey");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800657
Yingdi Yu2e57a582014-02-20 23:34:43 -0800658 // CFErrorRef error;
659 // SecTransformRef decrypt = SecDecryptTransformCreate(decryptKey, &error);
660 // if (error) throw Error("Fail to create decrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800661
Yingdi Yu2e57a582014-02-20 23:34:43 -0800662 // Boolean set_res = SecTransformSetAttribute(decrypt,
663 // kSecTransformInputAttributeName,
664 // dataRef,
665 // &error);
666 // if (error) throw Error("Fail to configure decrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800667
Yingdi Yu2e57a582014-02-20 23:34:43 -0800668 // CFDataRef output = (CFDataRef) SecTransformExecute(decrypt, &error);
669 // if (error)
670 // {
671 // CFShow(error);
672 // throw Error("Fail to decrypt data");
673 // }
674 // if (!output) throw Error("Output is NULL!\n");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800675
Yingdi Yu2e57a582014-02-20 23:34:43 -0800676 // return make_shared<Buffer>(CFDataGetBytePtr(output), CFDataGetLength(output));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800677}
678
Yingdi Yu2e57a582014-02-20 23:34:43 -0800679void
680SecTpmOsx::addAppToACL(const Name & keyName, KeyClass keyClass, const string & appPath, AclType acl)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800681{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800682 if(keyClass == KEY_CLASS_PRIVATE && acl == ACL_TYPE_PRIVATE)
683 {
684 SecKeychainItemRef privateKey = m_impl->getKey(keyName, keyClass);
685
686 SecAccessRef accRef;
687 OSStatus acc_res = SecKeychainItemCopyAccess(privateKey, &accRef);
688
689 CFArrayRef signACL = SecAccessCopyMatchingACLList(accRef,
690 kSecACLAuthorizationSign);
691
692 SecACLRef aclRef = (SecACLRef) CFArrayGetValueAtIndex(signACL, 0);
693
694 CFArrayRef appList;
695 CFStringRef description;
696 SecKeychainPromptSelector promptSelector;
697 OSStatus acl_res = SecACLCopyContents(aclRef,
698 &appList,
699 &description,
700 &promptSelector);
701
702 CFMutableArrayRef newAppList = CFArrayCreateMutableCopy(NULL,
703 0,
704 appList);
705
706 SecTrustedApplicationRef trustedApp;
707 acl_res = SecTrustedApplicationCreateFromPath(appPath.c_str(),
708 &trustedApp);
709
710 CFArrayAppendValue(newAppList, trustedApp);
711
712 acl_res = SecACLSetContents(aclRef,
713 newAppList,
714 description,
715 promptSelector);
716
717 acc_res = SecKeychainItemSetAccess(privateKey, accRef);
718 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800719}
720
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800721ConstBufferPtr
Yingdi Yufc40d872014-02-18 12:56:04 -0800722SecTpmOsx::encryptInTpm(const uint8_t* data, size_t dataLength, const Name & keyName, bool sym)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800723{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800724 throw Error("SecTpmOsx::encryptInTpm is not supported");
725 // _LOG_TRACE("OSXPrivateKeyStorage::Encrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800726
Yingdi Yu2e57a582014-02-20 23:34:43 -0800727 // KeyClass keyClass;
728 // if(sym)
729 // keyClass = KEY_CLASS_SYMMETRIC;
730 // else
731 // keyClass = KEY_CLASS_PUBLIC;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800732
Yingdi Yu2e57a582014-02-20 23:34:43 -0800733 // CFDataRef dataRef = CFDataCreate(NULL,
734 // reinterpret_cast<const unsigned char*>(data),
735 // dataLength
736 // );
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800737
Yingdi Yu2e57a582014-02-20 23:34:43 -0800738 // SecKeyRef encryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800739
Yingdi Yu2e57a582014-02-20 23:34:43 -0800740 // CFErrorRef error;
741 // SecTransformRef encrypt = SecEncryptTransformCreate(encryptKey, &error);
742 // if (error) throw Error("Fail to create encrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800743
Yingdi Yu2e57a582014-02-20 23:34:43 -0800744 // Boolean set_res = SecTransformSetAttribute(encrypt,
745 // kSecTransformInputAttributeName,
746 // dataRef,
747 // &error);
748 // if (error) throw Error("Fail to configure encrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800749
Yingdi Yu2e57a582014-02-20 23:34:43 -0800750 // CFDataRef output = (CFDataRef) SecTransformExecute(encrypt, &error);
751 // if (error) throw Error("Fail to encrypt data");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800752
Yingdi Yu2e57a582014-02-20 23:34:43 -0800753 // if (!output) throw Error("Output is NULL!\n");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800754
Yingdi Yu2e57a582014-02-20 23:34:43 -0800755 // return make_shared<Buffer> (CFDataGetBytePtr(output), CFDataGetLength(output));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800756}
757
758bool
759SecTpmOsx::doesKeyExistInTpm(const Name & keyName, KeyClass keyClass)
760{
761 _LOG_TRACE("OSXPrivateKeyStorage::doesKeyExist");
762
763 string keyNameUri = m_impl->toInternalKeyName(keyName, keyClass);
764
765 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
766 keyNameUri.c_str(),
767 kCFStringEncodingUTF8);
768
769 CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL,
770 4,
771 &kCFTypeDictionaryKeyCallBacks,
772 NULL);
773
774 CFDictionaryAddValue(attrDict, kSecClass, kSecClassKey);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800775 // CFDictionaryAddValue(attrDict, kSecAttrKeyClass, m_impl->getKeyClass(keyClass));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800776 CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
777 CFDictionaryAddValue(attrDict, kSecReturnRef, kCFBooleanTrue);
778
779 SecKeychainItemRef itemRef;
780 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict, (CFTypeRef*)&itemRef);
781
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800782 if(res == errSecSuccess)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800783 return true;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800784 else
785 return false;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800786
787}
788
Yingdi Yu4b752752014-02-18 12:24:03 -0800789bool
790SecTpmOsx::generateRandomBlock(uint8_t* res, size_t size)
791{
792 return (SecRandomCopyBytes(kSecRandomDefault, size, res) == 0);
793}
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800794
795////////////////////////////////
796// OSXPrivateKeyStorage::Impl //
797////////////////////////////////
798
799SecKeychainItemRef
800SecTpmOsx::Impl::getKey(const Name & keyName, KeyClass keyClass)
801{
802 string keyNameUri = toInternalKeyName(keyName, keyClass);
803
804 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
805 keyNameUri.c_str(),
806 kCFStringEncodingUTF8);
807
808 CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL,
809 5,
810 &kCFTypeDictionaryKeyCallBacks,
811 NULL);
812
813 CFDictionaryAddValue(attrDict, kSecClass, kSecClassKey);
814 CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
815 CFDictionaryAddValue(attrDict, kSecAttrKeyClass, getKeyClass(keyClass));
816 CFDictionaryAddValue(attrDict, kSecReturnRef, kCFBooleanTrue);
817
818 SecKeychainItemRef keyItem;
819
820 OSStatus res = SecItemCopyMatching((CFDictionaryRef) attrDict, (CFTypeRef*)&keyItem);
821
822 if(res != errSecSuccess){
823 _LOG_DEBUG("Fail to find the key!");
824 return NULL;
825 }
826 else
827 return keyItem;
828}
829
830string
831SecTpmOsx::Impl::toInternalKeyName(const Name & keyName, KeyClass keyClass)
832{
833 string keyUri = keyName.toUri();
834
835 if(KEY_CLASS_SYMMETRIC == keyClass)
836 return keyUri + "/symmetric";
837 else
838 return keyUri;
839}
840
841const CFTypeRef
842SecTpmOsx::Impl::getAsymKeyType(KeyType keyType)
843{
844 switch(keyType){
845 case KEY_TYPE_RSA:
846 return kSecAttrKeyTypeRSA;
847 default:
848 _LOG_DEBUG("Unrecognized key type!")
849 return NULL;
850 }
851}
852
853const CFTypeRef
854SecTpmOsx::Impl::getSymKeyType(KeyType keyType)
855{
856 switch(keyType){
857 case KEY_TYPE_AES:
858 return kSecAttrKeyTypeAES;
859 default:
860 _LOG_DEBUG("Unrecognized key type!")
861 return NULL;
862 }
863}
864
865const CFTypeRef
866SecTpmOsx::Impl::getKeyClass(KeyClass keyClass)
867{
868 switch(keyClass){
869 case KEY_CLASS_PRIVATE:
870 return kSecAttrKeyClassPrivate;
871 case KEY_CLASS_PUBLIC:
872 return kSecAttrKeyClassPublic;
873 case KEY_CLASS_SYMMETRIC:
874 return kSecAttrKeyClassSymmetric;
875 default:
876 _LOG_DEBUG("Unrecognized key class!");
877 return NULL;
878 }
879}
880
881const CFStringRef
882SecTpmOsx::Impl::getDigestAlgorithm(DigestAlgorithm digestAlgo)
883{
884 switch(digestAlgo){
Jeff Thompson2747dc02013-10-04 19:11:34 -0700885 // case DIGEST_MD2:
886 // return kSecDigestMD2;
887 // case DIGEST_MD5:
888 // return kSecDigestMD5;
889 // case DIGEST_SHA1:
890 // return kSecDigestSHA1;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800891 case DIGEST_ALGORITHM_SHA256:
892 return kSecDigestSHA2;
893 default:
894 _LOG_DEBUG("Unrecognized digest algorithm!");
895 return NULL;
Jeff Thompson2747dc02013-10-04 19:11:34 -0700896 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800897}
Jeff Thompson2747dc02013-10-04 19:11:34 -0700898
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800899long
900SecTpmOsx::Impl::getDigestSize(DigestAlgorithm digestAlgo)
901{
902 switch(digestAlgo){
903 case DIGEST_ALGORITHM_SHA256:
904 return 256;
Jeff Thompson2747dc02013-10-04 19:11:34 -0700905 // case DIGEST_SHA1:
906 // case DIGEST_MD2:
907 // case DIGEST_MD5:
908 // return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800909 default:
910 _LOG_DEBUG("Unrecognized digest algorithm! Unknown digest size");
911 return -1;
Jeff Thompson2747dc02013-10-04 19:11:34 -0700912 }
Jeff Thompson2747dc02013-10-04 19:11:34 -0700913}
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800914
Yingdi Yufc40d872014-02-18 12:56:04 -0800915} // namespace ndn