blob: 6436b5af75f4f2e8848f4a8c3d2cca164ee29b0d [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"
Yingdi Yu8dceb1d2014-02-18 12:45:10 -080014#include <cryptopp/files.h>
15#include <cryptopp/asn.h>
Jeff Thompson2747dc02013-10-04 19:11:34 -070016
Yingdi Yu2b2b4792014-02-04 16:27:07 -080017#include <pwd.h>
18#include <unistd.h>
19#include <stdlib.h>
20#include <string.h>
Jeff Thompson2747dc02013-10-04 19:11:34 -070021
Alexander Afanasyev04b22a92014-01-05 22:40:17 -080022#include <CoreFoundation/CoreFoundation.h>
23#include <Security/Security.h>
Yingdi Yu4b752752014-02-18 12:24:03 -080024#include <Security/SecRandom.h>
Alexander Afanasyev04b22a92014-01-05 22:40:17 -080025#include <CoreServices/CoreServices.h>
Jeff Thompson2747dc02013-10-04 19:11:34 -070026
27using namespace std;
Jeff Thompson2747dc02013-10-04 19:11:34 -070028
Yingdi Yu21157162014-02-28 13:02:34 -080029INIT_LOGGER("ndn.SecTpmOsx");
Jeff Thompson2747dc02013-10-04 19:11:34 -070030
Yingdi Yufc40d872014-02-18 12:56:04 -080031namespace ndn {
32
Yingdi Yu2b2b4792014-02-04 16:27:07 -080033class SecTpmOsx::Impl {
34public:
35 Impl()
Yingdi Yube4150e2014-02-18 13:02:46 -080036 : m_passwordSet(false)
37 , m_inTerminal(false)
Yingdi Yu2b2b4792014-02-04 16:27:07 -080038 {}
Alexander Afanasyev04b22a92014-01-05 22:40:17 -080039
Yingdi Yu2b2b4792014-02-04 16:27:07 -080040 /**
41 * @brief Convert NDN name of a key to internal name of the key.
42 *
Yingdi Yufc40d872014-02-18 12:56:04 -080043 * @param keyName
44 * @param keyClass
Yingdi Yu2b2b4792014-02-04 16:27:07 -080045 * @return the internal key name
46 */
47 std::string
48 toInternalKeyName(const Name & keyName, KeyClass keyClass);
Alexander Afanasyev04b22a92014-01-05 22:40:17 -080049
Yingdi Yu2b2b4792014-02-04 16:27:07 -080050 /**
51 * @brief Get key.
Yingdi Yufc40d872014-02-18 12:56:04 -080052 *
53 * @param keyName
54 * @param keyClass
Yingdi Yu2b2b4792014-02-04 16:27:07 -080055 * @returns pointer to the key
56 */
57 SecKeychainItemRef
58 getKey(const Name & keyName, KeyClass keyClass);
Alexander Afanasyev04b22a92014-01-05 22:40:17 -080059
Yingdi Yu2b2b4792014-02-04 16:27:07 -080060 /**
Yingdi Yufc40d872014-02-18 12:56:04 -080061 * @brief Convert keyType to MAC OS symmetric key key type
62 *
Yingdi Yu2b2b4792014-02-04 16:27:07 -080063 * @param keyType
64 * @returns MAC OS key type
65 */
Yingdi Yu87581582014-01-14 14:28:39 -080066 const CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -080067 getSymKeyType(KeyType keyType);
68
69 /**
Yingdi Yufc40d872014-02-18 12:56:04 -080070 * @brief Convert keyType to MAC OS asymmetirc key type
71 *
Yingdi Yu2b2b4792014-02-04 16:27:07 -080072 * @param keyType
73 * @returns MAC OS key type
74 */
Yingdi Yu87581582014-01-14 14:28:39 -080075 const CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -080076 getAsymKeyType(KeyType keyType);
77
78 /**
Yingdi Yufc40d872014-02-18 12:56:04 -080079 * @brief Convert keyClass to MAC OS key class
80 *
Yingdi Yu2b2b4792014-02-04 16:27:07 -080081 * @param keyClass
82 * @returns MAC OS key class
83 */
Yingdi Yu87581582014-01-14 14:28:39 -080084 const CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -080085 getKeyClass(KeyClass keyClass);
86
87 /**
Yingdi Yufc40d872014-02-18 12:56:04 -080088 * @brief Convert digestAlgo to MAC OS algorithm id
89 *
Yingdi Yu2b2b4792014-02-04 16:27:07 -080090 * @param digestAlgo
91 * @returns MAC OS algorithm id
92 */
Yingdi Yu87581582014-01-14 14:28:39 -080093 const CFStringRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -080094 getDigestAlgorithm(DigestAlgorithm digestAlgo);
95
96 /**
Yingdi Yufc40d872014-02-18 12:56:04 -080097 * @brief Get the digest size of the corresponding algorithm
98 *
99 * @param digestAlgo
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800100 * @return digest size
101 */
102 long
103 getDigestSize(DigestAlgorithm digestAlgo);
104
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800105 ///////////////////////////////////////////////
106 // everything here is public, including data //
107 ///////////////////////////////////////////////
108public:
109 SecKeychainRef m_keyChainRef;
Yingdi Yube4150e2014-02-18 13:02:46 -0800110 bool m_passwordSet;
111 string m_password;
112 bool m_inTerminal;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800113};
114
115
116SecTpmOsx::SecTpmOsx()
117 : m_impl(new Impl)
118{
Yingdi Yube4150e2014-02-18 13:02:46 -0800119 if(m_impl->m_inTerminal)
120 SecKeychainSetUserInteractionAllowed (false);
121 else
122 SecKeychainSetUserInteractionAllowed (true);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800123
Yingdi Yube4150e2014-02-18 13:02:46 -0800124 OSStatus res = SecKeychainCopyDefault(&m_impl->m_keyChainRef);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800125
126 if (res == errSecNoDefaultKeychain) //If no default key chain, create one.
Yingdi Yube4150e2014-02-18 13:02:46 -0800127 throw Error("No default keychain, create one first!");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800128}
129
130SecTpmOsx::~SecTpmOsx(){
131 //TODO: implement
132}
133
Yingdi Yube4150e2014-02-18 13:02:46 -0800134void
135SecTpmOsx::setTpmPassword(const uint8_t* password, size_t passwordLength)
136{
137 m_impl->m_passwordSet = true;
138 memset(const_cast<char*>(m_impl->m_password.c_str()), 0, m_impl->m_password.size());
139 m_impl->m_password.clear();
140 m_impl->m_password.append(reinterpret_cast<const char*>(password), passwordLength);
141}
142
143void
144SecTpmOsx::resetTpmPassword()
145{
146 m_impl->m_passwordSet = false;
147 memset(const_cast<char*>(m_impl->m_password.c_str()), 0, m_impl->m_password.size());
148 m_impl->m_password.clear();
149}
150
151void
152SecTpmOsx::setInTerminal(bool inTerminal)
153{
154 m_impl->m_inTerminal = inTerminal;
155 if(inTerminal)
156 SecKeychainSetUserInteractionAllowed (false);
157 else
158 SecKeychainSetUserInteractionAllowed (true);
159}
160
161bool
162SecTpmOsx::getInTerminal()
163{
164 return m_impl->m_inTerminal;
165}
166
167bool
168SecTpmOsx::locked()
169{
170 SecKeychainStatus keychainStatus;
171
172 OSStatus res = SecKeychainGetStatus(m_impl->m_keyChainRef, &keychainStatus);
173 if(res != errSecSuccess)
174 return true;
175 else
176 return ((kSecUnlockStateStatus & keychainStatus) == 0);
177}
178
Yingdi Yu2e57a582014-02-20 23:34:43 -0800179bool
Yingdi Yube4150e2014-02-18 13:02:46 -0800180SecTpmOsx::unlockTpm(const char* password, size_t passwordLength, bool usePassword)
181{
182 OSStatus res;
183
184 // If the default key chain is already unlocked, return immediately.
185 if(!locked())
Yingdi Yu2e57a582014-02-20 23:34:43 -0800186 return true;
Yingdi Yube4150e2014-02-18 13:02:46 -0800187
188 // If the default key chain is locked, unlock the key chain.
189 if(usePassword)
190 {
191 // Use the supplied password.
192 res = SecKeychainUnlock(m_impl->m_keyChainRef,
193 passwordLength,
194 password,
195 true);
196 }
197 else if(m_impl->m_passwordSet)
198 {
199 // If no password supplied, then use the configured password if exists.
200 SecKeychainUnlock(m_impl->m_keyChainRef,
201 m_impl->m_password.size(),
202 m_impl->m_password.c_str(),
203 true);
204 }
205 else if(m_impl->m_inTerminal)
206 {
207 // If no configured password, get password from terminal if inTerminal set.
208 bool locked = true;
209 const char* fmt = "Password to unlock the default keychain: ";
210 int count = 0;
211
212 while(locked)
213 {
214 if(count > 2)
215 break;
216
217 char* getPassword = NULL;
218 getPassword = getpass(fmt);
219 count++;
220
221 if (!getPassword)
222 continue;
223
224 res = SecKeychainUnlock(m_impl->m_keyChainRef,
225 strlen(getPassword),
226 getPassword,
227 true);
228
229 memset(getPassword, 0, strlen(getPassword));
230
231 if(res == errSecSuccess)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800232 break;
Yingdi Yube4150e2014-02-18 13:02:46 -0800233 }
234 }
235 else
236 {
237 // If inTerminal is not set, get the password from GUI.
238 SecKeychainUnlock(m_impl->m_keyChainRef, 0, 0, false);
239 }
Yingdi Yu2e57a582014-02-20 23:34:43 -0800240
241 return !locked();
Yingdi Yube4150e2014-02-18 13:02:46 -0800242}
243
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800244void
Yingdi Yube4150e2014-02-18 13:02:46 -0800245SecTpmOsx::generateKeyPairInTpmInternal(const Name & keyName, KeyType keyType, int keySize, bool retry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800246{
247
248 if(doesKeyExistInTpm(keyName, KEY_CLASS_PUBLIC)){
249 _LOG_DEBUG("keyName has existed");
250 throw Error("keyName has existed");
251 }
252
253 string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_PUBLIC);
254
255 SecKeyRef publicKey, privateKey;
256
257 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
258 keyNameUri.c_str(),
259 kCFStringEncodingUTF8);
260
261 CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL,
262 3,
263 &kCFTypeDictionaryKeyCallBacks,
264 NULL);
265
266 CFDictionaryAddValue(attrDict, kSecAttrKeyType, m_impl->getAsymKeyType(keyType));
267 CFDictionaryAddValue(attrDict, kSecAttrKeySizeInBits, CFNumberCreate(NULL, kCFNumberIntType, &keySize));
268 CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
269
270 OSStatus res = SecKeyGeneratePair((CFDictionaryRef)attrDict, &publicKey, &privateKey);
271
Yingdi Yube4150e2014-02-18 13:02:46 -0800272 if (res == errSecSuccess)
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800273 {
274 CFRelease(publicKey);
275 CFRelease(privateKey);
276 return;
277 }
Yingdi Yube4150e2014-02-18 13:02:46 -0800278
279 if (res == errSecAuthFailed && !retry)
280 {
Yingdi Yu2e57a582014-02-20 23:34:43 -0800281 if(unlockTpm(0, 0, false))
282 generateKeyPairInTpmInternal(keyName, keyType, keySize, true);
283 else
284 throw Error("Fail to unlock the keychain");
Yingdi Yube4150e2014-02-18 13:02:46 -0800285 }
286 else
287 {
288 _LOG_DEBUG("Fail to create a key pair: " << res);
289 throw Error("Fail to create a key pair");
290 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800291}
292
293void
Yingdi Yube4150e2014-02-18 13:02:46 -0800294SecTpmOsx::deleteKeyPairInTpmInternal(const Name &keyName, bool retry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800295{
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800296 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800297 keyName.toUri().c_str(),
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800298 kCFStringEncodingUTF8);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800299
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800300 CFMutableDictionaryRef searchDict =
301 CFDictionaryCreateMutable(NULL, 5, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800302
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800303 CFDictionaryAddValue(searchDict, kSecClass, kSecClassKey);
304 CFDictionaryAddValue(searchDict, kSecAttrLabel, keyLabel);
305 CFDictionaryAddValue(searchDict, kSecMatchLimit, kSecMatchLimitAll);
Yingdi Yube4150e2014-02-18 13:02:46 -0800306 OSStatus res = SecItemDelete(searchDict);
307
308 if (res == errSecSuccess)
309 return;
310
311 if (res == errSecAuthFailed && !retry)
312 {
Yingdi Yu2e57a582014-02-20 23:34:43 -0800313 if(unlockTpm(0, 0, false))
314 deleteKeyPairInTpmInternal(keyName, true);
Yingdi Yube4150e2014-02-18 13:02:46 -0800315 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800316}
317
318void
319SecTpmOsx::generateSymmetricKeyInTpm(const Name & keyName, KeyType keyType, int keySize)
320{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800321 throw Error("SecTpmOsx::generateSymmetricKeyInTpm is not supported");
322 // if(doesKeyExistInTpm(keyName, KEY_CLASS_SYMMETRIC))
323 // throw Error("keyName has existed!");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800324
Yingdi Yu2e57a582014-02-20 23:34:43 -0800325 // string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_SYMMETRIC);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800326
Yingdi Yu2e57a582014-02-20 23:34:43 -0800327 // CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
328 // 0,
329 // &kCFTypeDictionaryKeyCallBacks,
330 // &kCFTypeDictionaryValueCallBacks);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800331
Yingdi Yu2e57a582014-02-20 23:34:43 -0800332 // CFStringRef keyLabel = CFStringCreateWithCString(NULL,
333 // keyNameUri.c_str(),
334 // kCFStringEncodingUTF8);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800335
Yingdi Yu2e57a582014-02-20 23:34:43 -0800336 // CFDictionaryAddValue(attrDict, kSecAttrKeyType, m_impl->getSymKeyType(keyType));
337 // CFDictionaryAddValue(attrDict, kSecAttrKeySizeInBits, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &keySize));
338 // CFDictionaryAddValue(attrDict, kSecAttrIsPermanent, kCFBooleanTrue);
339 // CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800340
Yingdi Yu2e57a582014-02-20 23:34:43 -0800341 // CFErrorRef error = NULL;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800342
Yingdi Yu2e57a582014-02-20 23:34:43 -0800343 // SecKeyRef symmetricKey = SecKeyGenerateSymmetric(attrDict, &error);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800344
Yingdi Yu2e57a582014-02-20 23:34:43 -0800345 // if (error)
346 // throw Error("Fail to create a symmetric key");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800347}
348
Yingdi Yu2e57a582014-02-20 23:34:43 -0800349shared_ptr<PublicKey>
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800350SecTpmOsx::getPublicKeyFromTpm(const Name & keyName)
351{
352 _LOG_TRACE("OSXPrivateKeyStorage::getPublickey");
353
354 SecKeychainItemRef publicKey = m_impl->getKey(keyName, KEY_CLASS_PUBLIC);
355
356 CFDataRef exportedKey;
357
358 OSStatus res = SecItemExport(publicKey,
359 kSecFormatOpenSSL,
360 0,
361 NULL,
362 &exportedKey);
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800363 if (res != errSecSuccess)
364 {
365 throw Error("Cannot export requested public key from OSX Keychain");
366 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800367
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800368 shared_ptr<PublicKey> key = make_shared<PublicKey>(CFDataGetBytePtr(exportedKey), CFDataGetLength(exportedKey));
369 CFRelease(exportedKey);
370 return key;
371}
372
373ConstBufferPtr
Yingdi Yube4150e2014-02-18 13:02:46 -0800374SecTpmOsx::exportPrivateKeyPkcs1FromTpmInternal(const Name& keyName, bool retry)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800375{
376 using namespace CryptoPP;
377
378 SecKeychainItemRef privateKey = m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
379 CFDataRef exportedKey;
380 OSStatus res = SecItemExport(privateKey,
381 kSecFormatOpenSSL,
382 0,
383 NULL,
384 &exportedKey);
385
386 if(res != errSecSuccess)
387 {
Yingdi Yube4150e2014-02-18 13:02:46 -0800388 if(res == errSecAuthFailed && !retry)
389 {
Yingdi Yu2e57a582014-02-20 23:34:43 -0800390 if(unlockTpm(0, 0, false))
391 return exportPrivateKeyPkcs1FromTpmInternal(keyName, true);
392 else
393 return shared_ptr<Buffer>();
Yingdi Yube4150e2014-02-18 13:02:46 -0800394 }
395 else
396 return shared_ptr<Buffer>();
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800397 }
398
399 OBufferStream pkcs1Os;
400 FileSink sink(pkcs1Os);
401
402 uint32_t version = 0;
403 OID algorithm("1.2.840.113549.1.1.1");
404 SecByteBlock rawKeyBits;
405 // PrivateKeyInfo ::= SEQUENCE {
406 // version INTEGER,
407 // privateKeyAlgorithm SEQUENCE,
408 // privateKey OCTECT STRING}
409 DERSequenceEncoder privateKeyInfo(sink);
410 {
411 DEREncodeUnsigned<uint32_t>(privateKeyInfo, version, INTEGER);
412 DERSequenceEncoder privateKeyAlgorithm(privateKeyInfo);
413 {
414 algorithm.encode(privateKeyAlgorithm);
415 DEREncodeNull(privateKeyAlgorithm);
416 }
417 privateKeyAlgorithm.MessageEnd();
418 DEREncodeOctetString(privateKeyInfo, CFDataGetBytePtr(exportedKey), CFDataGetLength(exportedKey));
419 }
420 privateKeyInfo.MessageEnd();
421
422 CFRelease(exportedKey);
423 return pkcs1Os.buf();
424}
425
426bool
Yingdi Yube4150e2014-02-18 13:02:46 -0800427SecTpmOsx::importPrivateKeyPkcs1IntoTpmInternal(const Name& keyName, const uint8_t* buf, size_t size, bool retry)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800428{
429 using namespace CryptoPP;
430
431 StringSource privateKeySource(buf, size, true);
432 uint32_t tmpNum;
433 OID tmpOID;
434 SecByteBlock rawKeyBits;
435 // PrivateKeyInfo ::= SEQUENCE {
436 // INTEGER,
437 // SEQUENCE,
438 // OCTECT STRING}
439 BERSequenceDecoder privateKeyInfo(privateKeySource);
440 {
441 BERDecodeUnsigned<uint32_t>(privateKeyInfo, tmpNum, INTEGER);
442 BERSequenceDecoder sequenceDecoder(privateKeyInfo);
443 {
444 tmpOID.decode(sequenceDecoder);
445 BERDecodeNull(sequenceDecoder);
446 }
447 BERDecodeOctetString(privateKeyInfo, rawKeyBits);
448 }
449 privateKeyInfo.MessageEnd();
450
451 CFDataRef importedKey = CFDataCreateWithBytesNoCopy(NULL,
452 rawKeyBits.BytePtr(),
453 rawKeyBits.size(),
454 kCFAllocatorNull);
455
456 SecExternalFormat externalFormat = kSecFormatOpenSSL;
457 SecExternalItemType externalType = kSecItemTypePrivateKey;
458 SecKeyImportExportParameters keyParams;
459 memset(&keyParams, 0, sizeof(keyParams));
460 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
461 keyParams.keyAttributes = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT;
462 SecAccessRef access;
463 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
464 keyName.toUri().c_str(),
465 kCFStringEncodingUTF8);
466 SecAccessCreate(keyLabel, NULL, &access);
467 keyParams.accessRef = access;
468 CFArrayRef outItems;
469
470 OSStatus res = SecKeychainItemImport (importedKey,
471 NULL,
472 &externalFormat,
473 &externalType,
474 0,
475 &keyParams,
476 m_impl->m_keyChainRef,
477 &outItems);
478
479 if(res != errSecSuccess)
480 {
Yingdi Yube4150e2014-02-18 13:02:46 -0800481 if(res == errSecAuthFailed && !retry)
482 {
Yingdi Yu2e57a582014-02-20 23:34:43 -0800483 if(unlockTpm(0, 0, false))
484 return importPrivateKeyPkcs1IntoTpmInternal(keyName, buf, size, true);
485 else
486 return false;
Yingdi Yube4150e2014-02-18 13:02:46 -0800487 }
488 else
489 return false;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800490 }
491
492 SecKeychainItemRef privateKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems, 0);
493 SecKeychainAttribute attrs[1]; // maximum number of attributes
494 SecKeychainAttributeList attrList = { 0, attrs };
495 string keyUri = keyName.toUri();
496 {
497 attrs[attrList.count].tag = kSecKeyPrintName;
498 attrs[attrList.count].length = keyUri.size();
499 attrs[attrList.count].data = (void *)keyUri.c_str();
500 attrList.count++;
501 }
502
503 res = SecKeychainItemModifyAttributesAndData(privateKey,
504 &attrList,
505 0,
506 NULL);
507
508 if(res != errSecSuccess)
509 {
510 return false;
511 }
512
513 CFRelease(importedKey);
514 return true;
515}
516
517bool
518SecTpmOsx::importPublicKeyPkcs1IntoTpm(const Name& keyName, const uint8_t* buf, size_t size)
519{
520 CFDataRef importedKey = CFDataCreateWithBytesNoCopy(NULL,
521 buf,
522 size,
523 kCFAllocatorNull);
524
525 SecExternalFormat externalFormat = kSecFormatOpenSSL;
526 SecExternalItemType externalType = kSecItemTypePublicKey;
527 CFArrayRef outItems;
528
529 OSStatus res = SecItemImport (importedKey,
530 NULL,
531 &externalFormat,
532 &externalType,
533 0,
534 NULL,
535 m_impl->m_keyChainRef,
536 &outItems);
537
538 if(res != errSecSuccess)
539 return false;
540
541 SecKeychainItemRef publicKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems, 0);
542 SecKeychainAttribute attrs[1]; // maximum number of attributes
543 SecKeychainAttributeList attrList = { 0, attrs };
544 string keyUri = keyName.toUri();
545 {
546 attrs[attrList.count].tag = kSecKeyPrintName;
547 attrs[attrList.count].length = keyUri.size();
548 attrs[attrList.count].data = (void *)keyUri.c_str();
549 attrList.count++;
550 }
551
552 res = SecKeychainItemModifyAttributesAndData(publicKey,
553 &attrList,
554 0,
555 NULL);
556
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800557 if(res != errSecSuccess)
558 return false;
559
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800560 CFRelease(importedKey);
561 return true;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800562}
563
564Block
Yingdi Yube4150e2014-02-18 13:02:46 -0800565SecTpmOsx::signInTpmInternal(const uint8_t *data, size_t dataLength, const Name& keyName, DigestAlgorithm digestAlgorithm, bool retry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800566{
567 _LOG_TRACE("OSXPrivateKeyStorage::Sign");
568
569 CFDataRef dataRef = CFDataCreateWithBytesNoCopy(NULL,
570 data,
571 dataLength,
Yingdi Yu2e57a582014-02-20 23:34:43 -0800572 kCFAllocatorNull);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800573
574 SecKeyRef privateKey = (SecKeyRef)m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
575
576 CFErrorRef error;
577 SecTransformRef signer = SecSignTransformCreate((SecKeyRef)privateKey, &error);
578 if (error) throw Error("Fail to create signer");
579
580 // Set input
581 Boolean set_res = SecTransformSetAttribute(signer,
582 kSecTransformInputAttributeName,
583 dataRef,
584 &error);
585 if (error) throw Error("Fail to configure input of signer");
586
587 // Enable use of padding
Yingdi Yu2e57a582014-02-20 23:34:43 -0800588 SecTransformSetAttribute(signer,
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800589 kSecPaddingKey,
590 kSecPaddingPKCS1Key,
591 &error);
592 if (error) throw Error("Fail to configure digest algorithm of signer");
593
594 // Set padding type
595 set_res = SecTransformSetAttribute(signer,
596 kSecDigestTypeAttribute,
597 m_impl->getDigestAlgorithm(digestAlgorithm),
598 &error);
599 if (error) throw Error("Fail to configure digest algorithm of signer");
600
601 // Set padding attribute
602 long digestSize = m_impl->getDigestSize(digestAlgorithm);
603 set_res = SecTransformSetAttribute(signer,
604 kSecDigestLengthAttribute,
605 CFNumberCreate(NULL, kCFNumberLongType, &digestSize),
606 &error);
607 if (error) throw Error("Fail to configure digest size of signer");
608
609 // Actually sign
610 CFDataRef signature = (CFDataRef) SecTransformExecute(signer, &error);
Yingdi Yube4150e2014-02-18 13:02:46 -0800611 if (error)
612 {
613 if(!retry)
614 {
Yingdi Yu2e57a582014-02-20 23:34:43 -0800615 if(unlockTpm(0, 0, false))
616 return signInTpmInternal(data, dataLength, keyName, digestAlgorithm, true);
617 else
618 throw Error("Fail to unlock the keychain");
Yingdi Yube4150e2014-02-18 13:02:46 -0800619 }
620 else
621 {
622 CFShow(error);
623 throw Error("Fail to sign data");
624 }
625 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800626
627 if (!signature) throw Error("Signature is NULL!\n");
628
629 return Block(Tlv::SignatureValue,
Yingdi Yu2e57a582014-02-20 23:34:43 -0800630 make_shared<Buffer>(CFDataGetBytePtr(signature), CFDataGetLength(signature)));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800631}
632
633ConstBufferPtr
Yingdi Yufc40d872014-02-18 12:56:04 -0800634SecTpmOsx::decryptInTpm(const uint8_t* data, size_t dataLength, const Name & keyName, bool sym)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800635{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800636 throw Error("SecTpmOsx::decryptInTpm is not supported");
637 // _LOG_TRACE("OSXPrivateKeyStorage::Decrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800638
Yingdi Yu2e57a582014-02-20 23:34:43 -0800639 // KeyClass keyClass;
640 // if(sym)
641 // keyClass = KEY_CLASS_SYMMETRIC;
642 // else
643 // keyClass = KEY_CLASS_PRIVATE;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800644
Yingdi Yu2e57a582014-02-20 23:34:43 -0800645 // CFDataRef dataRef = CFDataCreate(NULL,
646 // reinterpret_cast<const unsigned char*>(data),
647 // dataLength
648 // );
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800649
Yingdi Yu2e57a582014-02-20 23:34:43 -0800650 // // _LOG_DEBUG("CreateData");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800651
Yingdi Yu2e57a582014-02-20 23:34:43 -0800652 // SecKeyRef decryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800653
Yingdi Yu2e57a582014-02-20 23:34:43 -0800654 // // _LOG_DEBUG("GetKey");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800655
Yingdi Yu2e57a582014-02-20 23:34:43 -0800656 // CFErrorRef error;
657 // SecTransformRef decrypt = SecDecryptTransformCreate(decryptKey, &error);
658 // if (error) throw Error("Fail to create decrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800659
Yingdi Yu2e57a582014-02-20 23:34:43 -0800660 // Boolean set_res = SecTransformSetAttribute(decrypt,
661 // kSecTransformInputAttributeName,
662 // dataRef,
663 // &error);
664 // if (error) throw Error("Fail to configure decrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800665
Yingdi Yu2e57a582014-02-20 23:34:43 -0800666 // CFDataRef output = (CFDataRef) SecTransformExecute(decrypt, &error);
667 // if (error)
668 // {
669 // CFShow(error);
670 // throw Error("Fail to decrypt data");
671 // }
672 // if (!output) throw Error("Output is NULL!\n");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800673
Yingdi Yu2e57a582014-02-20 23:34:43 -0800674 // return make_shared<Buffer>(CFDataGetBytePtr(output), CFDataGetLength(output));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800675}
676
Yingdi Yu2e57a582014-02-20 23:34:43 -0800677void
678SecTpmOsx::addAppToACL(const Name & keyName, KeyClass keyClass, const string & appPath, AclType acl)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800679{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800680 if(keyClass == KEY_CLASS_PRIVATE && acl == ACL_TYPE_PRIVATE)
681 {
682 SecKeychainItemRef privateKey = m_impl->getKey(keyName, keyClass);
683
684 SecAccessRef accRef;
685 OSStatus acc_res = SecKeychainItemCopyAccess(privateKey, &accRef);
686
687 CFArrayRef signACL = SecAccessCopyMatchingACLList(accRef,
688 kSecACLAuthorizationSign);
689
690 SecACLRef aclRef = (SecACLRef) CFArrayGetValueAtIndex(signACL, 0);
691
692 CFArrayRef appList;
693 CFStringRef description;
694 SecKeychainPromptSelector promptSelector;
695 OSStatus acl_res = SecACLCopyContents(aclRef,
696 &appList,
697 &description,
698 &promptSelector);
699
700 CFMutableArrayRef newAppList = CFArrayCreateMutableCopy(NULL,
701 0,
702 appList);
703
704 SecTrustedApplicationRef trustedApp;
705 acl_res = SecTrustedApplicationCreateFromPath(appPath.c_str(),
706 &trustedApp);
707
708 CFArrayAppendValue(newAppList, trustedApp);
709
710 acl_res = SecACLSetContents(aclRef,
711 newAppList,
712 description,
713 promptSelector);
714
715 acc_res = SecKeychainItemSetAccess(privateKey, accRef);
716 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800717}
718
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800719ConstBufferPtr
Yingdi Yufc40d872014-02-18 12:56:04 -0800720SecTpmOsx::encryptInTpm(const uint8_t* data, size_t dataLength, const Name & keyName, bool sym)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800721{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800722 throw Error("SecTpmOsx::encryptInTpm is not supported");
723 // _LOG_TRACE("OSXPrivateKeyStorage::Encrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800724
Yingdi Yu2e57a582014-02-20 23:34:43 -0800725 // KeyClass keyClass;
726 // if(sym)
727 // keyClass = KEY_CLASS_SYMMETRIC;
728 // else
729 // keyClass = KEY_CLASS_PUBLIC;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800730
Yingdi Yu2e57a582014-02-20 23:34:43 -0800731 // CFDataRef dataRef = CFDataCreate(NULL,
732 // reinterpret_cast<const unsigned char*>(data),
733 // dataLength
734 // );
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800735
Yingdi Yu2e57a582014-02-20 23:34:43 -0800736 // SecKeyRef encryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800737
Yingdi Yu2e57a582014-02-20 23:34:43 -0800738 // CFErrorRef error;
739 // SecTransformRef encrypt = SecEncryptTransformCreate(encryptKey, &error);
740 // if (error) throw Error("Fail to create encrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800741
Yingdi Yu2e57a582014-02-20 23:34:43 -0800742 // Boolean set_res = SecTransformSetAttribute(encrypt,
743 // kSecTransformInputAttributeName,
744 // dataRef,
745 // &error);
746 // if (error) throw Error("Fail to configure encrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800747
Yingdi Yu2e57a582014-02-20 23:34:43 -0800748 // CFDataRef output = (CFDataRef) SecTransformExecute(encrypt, &error);
749 // if (error) throw Error("Fail to encrypt data");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800750
Yingdi Yu2e57a582014-02-20 23:34:43 -0800751 // if (!output) throw Error("Output is NULL!\n");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800752
Yingdi Yu2e57a582014-02-20 23:34:43 -0800753 // return make_shared<Buffer> (CFDataGetBytePtr(output), CFDataGetLength(output));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800754}
755
756bool
757SecTpmOsx::doesKeyExistInTpm(const Name & keyName, KeyClass keyClass)
758{
759 _LOG_TRACE("OSXPrivateKeyStorage::doesKeyExist");
760
761 string keyNameUri = m_impl->toInternalKeyName(keyName, keyClass);
762
763 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
764 keyNameUri.c_str(),
765 kCFStringEncodingUTF8);
766
767 CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL,
768 4,
769 &kCFTypeDictionaryKeyCallBacks,
770 NULL);
771
772 CFDictionaryAddValue(attrDict, kSecClass, kSecClassKey);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800773 // CFDictionaryAddValue(attrDict, kSecAttrKeyClass, m_impl->getKeyClass(keyClass));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800774 CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
775 CFDictionaryAddValue(attrDict, kSecReturnRef, kCFBooleanTrue);
776
777 SecKeychainItemRef itemRef;
778 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict, (CFTypeRef*)&itemRef);
779
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800780 if(res == errSecSuccess)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800781 return true;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800782 else
783 return false;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800784
785}
786
Yingdi Yu4b752752014-02-18 12:24:03 -0800787bool
788SecTpmOsx::generateRandomBlock(uint8_t* res, size_t size)
789{
790 return (SecRandomCopyBytes(kSecRandomDefault, size, res) == 0);
791}
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800792
793////////////////////////////////
794// OSXPrivateKeyStorage::Impl //
795////////////////////////////////
796
797SecKeychainItemRef
798SecTpmOsx::Impl::getKey(const Name & keyName, KeyClass keyClass)
799{
800 string keyNameUri = toInternalKeyName(keyName, keyClass);
801
802 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
803 keyNameUri.c_str(),
804 kCFStringEncodingUTF8);
805
806 CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL,
807 5,
808 &kCFTypeDictionaryKeyCallBacks,
809 NULL);
810
811 CFDictionaryAddValue(attrDict, kSecClass, kSecClassKey);
812 CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
813 CFDictionaryAddValue(attrDict, kSecAttrKeyClass, getKeyClass(keyClass));
814 CFDictionaryAddValue(attrDict, kSecReturnRef, kCFBooleanTrue);
815
816 SecKeychainItemRef keyItem;
817
818 OSStatus res = SecItemCopyMatching((CFDictionaryRef) attrDict, (CFTypeRef*)&keyItem);
819
820 if(res != errSecSuccess){
821 _LOG_DEBUG("Fail to find the key!");
822 return NULL;
823 }
824 else
825 return keyItem;
826}
827
828string
829SecTpmOsx::Impl::toInternalKeyName(const Name & keyName, KeyClass keyClass)
830{
831 string keyUri = keyName.toUri();
832
833 if(KEY_CLASS_SYMMETRIC == keyClass)
834 return keyUri + "/symmetric";
835 else
836 return keyUri;
837}
838
839const CFTypeRef
840SecTpmOsx::Impl::getAsymKeyType(KeyType keyType)
841{
842 switch(keyType){
843 case KEY_TYPE_RSA:
844 return kSecAttrKeyTypeRSA;
845 default:
846 _LOG_DEBUG("Unrecognized key type!")
847 return NULL;
848 }
849}
850
851const CFTypeRef
852SecTpmOsx::Impl::getSymKeyType(KeyType keyType)
853{
854 switch(keyType){
855 case KEY_TYPE_AES:
856 return kSecAttrKeyTypeAES;
857 default:
858 _LOG_DEBUG("Unrecognized key type!")
859 return NULL;
860 }
861}
862
863const CFTypeRef
864SecTpmOsx::Impl::getKeyClass(KeyClass keyClass)
865{
866 switch(keyClass){
867 case KEY_CLASS_PRIVATE:
868 return kSecAttrKeyClassPrivate;
869 case KEY_CLASS_PUBLIC:
870 return kSecAttrKeyClassPublic;
871 case KEY_CLASS_SYMMETRIC:
872 return kSecAttrKeyClassSymmetric;
873 default:
874 _LOG_DEBUG("Unrecognized key class!");
875 return NULL;
876 }
877}
878
879const CFStringRef
880SecTpmOsx::Impl::getDigestAlgorithm(DigestAlgorithm digestAlgo)
881{
882 switch(digestAlgo){
Jeff Thompson2747dc02013-10-04 19:11:34 -0700883 // case DIGEST_MD2:
884 // return kSecDigestMD2;
885 // case DIGEST_MD5:
886 // return kSecDigestMD5;
887 // case DIGEST_SHA1:
888 // return kSecDigestSHA1;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800889 case DIGEST_ALGORITHM_SHA256:
890 return kSecDigestSHA2;
891 default:
892 _LOG_DEBUG("Unrecognized digest algorithm!");
893 return NULL;
Jeff Thompson2747dc02013-10-04 19:11:34 -0700894 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800895}
Jeff Thompson2747dc02013-10-04 19:11:34 -0700896
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800897long
898SecTpmOsx::Impl::getDigestSize(DigestAlgorithm digestAlgo)
899{
900 switch(digestAlgo){
901 case DIGEST_ALGORITHM_SHA256:
902 return 256;
Jeff Thompson2747dc02013-10-04 19:11:34 -0700903 // case DIGEST_SHA1:
904 // case DIGEST_MD2:
905 // case DIGEST_MD5:
906 // return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800907 default:
908 _LOG_DEBUG("Unrecognized digest algorithm! Unknown digest size");
909 return -1;
Jeff Thompson2747dc02013-10-04 19:11:34 -0700910 }
Jeff Thompson2747dc02013-10-04 19:11:34 -0700911}
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800912
Yingdi Yufc40d872014-02-18 12:56:04 -0800913} // namespace ndn