blob: de95ef1d27f3adab4ad182ade3ab1110243919db [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 Yu87581582014-01-14 14:28:39 -080029INIT_LOGGER("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
179void
180SecTpmOsx::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())
186 return;
187
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)
232 return;
233 }
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 }
240}
241
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800242void
Yingdi Yube4150e2014-02-18 13:02:46 -0800243SecTpmOsx::generateKeyPairInTpmInternal(const Name & keyName, KeyType keyType, int keySize, bool retry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800244{
245
246 if(doesKeyExistInTpm(keyName, KEY_CLASS_PUBLIC)){
247 _LOG_DEBUG("keyName has existed");
248 throw Error("keyName has existed");
249 }
250
251 string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_PUBLIC);
252
253 SecKeyRef publicKey, privateKey;
254
255 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
256 keyNameUri.c_str(),
257 kCFStringEncodingUTF8);
258
259 CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL,
260 3,
261 &kCFTypeDictionaryKeyCallBacks,
262 NULL);
263
264 CFDictionaryAddValue(attrDict, kSecAttrKeyType, m_impl->getAsymKeyType(keyType));
265 CFDictionaryAddValue(attrDict, kSecAttrKeySizeInBits, CFNumberCreate(NULL, kCFNumberIntType, &keySize));
266 CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
267
268 OSStatus res = SecKeyGeneratePair((CFDictionaryRef)attrDict, &publicKey, &privateKey);
269
Yingdi Yube4150e2014-02-18 13:02:46 -0800270 if (res == errSecSuccess)
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800271 {
272 CFRelease(publicKey);
273 CFRelease(privateKey);
274 return;
275 }
Yingdi Yube4150e2014-02-18 13:02:46 -0800276
277 if (res == errSecAuthFailed && !retry)
278 {
279 unlockTpm(0, 0, false);
280 generateKeyPairInTpmInternal(keyName, keyType, keySize, true);
281 }
282 else
283 {
284 _LOG_DEBUG("Fail to create a key pair: " << res);
285 throw Error("Fail to create a key pair");
286 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800287}
288
289void
Yingdi Yube4150e2014-02-18 13:02:46 -0800290SecTpmOsx::deleteKeyPairInTpmInternal(const Name &keyName, bool retry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800291{
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800292 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800293 keyName.toUri().c_str(),
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800294 kCFStringEncodingUTF8);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800295
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800296 CFMutableDictionaryRef searchDict =
297 CFDictionaryCreateMutable(NULL, 5, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800298
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800299 CFDictionaryAddValue(searchDict, kSecClass, kSecClassKey);
300 CFDictionaryAddValue(searchDict, kSecAttrLabel, keyLabel);
301 CFDictionaryAddValue(searchDict, kSecMatchLimit, kSecMatchLimitAll);
Yingdi Yube4150e2014-02-18 13:02:46 -0800302 OSStatus res = SecItemDelete(searchDict);
303
304 if (res == errSecSuccess)
305 return;
306
307 if (res == errSecAuthFailed && !retry)
308 {
309 unlockTpm(0, 0, false);
310 deleteKeyPairInTpmInternal(keyName, true);
311 }
312 else
313 {
314 _LOG_DEBUG("Fail to delete a key pair: " << res);
315 throw Error("Fail to delete a key pair");
316 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800317}
318
319void
320SecTpmOsx::generateSymmetricKeyInTpm(const Name & keyName, KeyType keyType, int keySize)
321{
322
323 if(doesKeyExistInTpm(keyName, KEY_CLASS_SYMMETRIC))
324 throw Error("keyName has existed!");
325
326 string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_SYMMETRIC);
327
328 CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
329 0,
330 &kCFTypeDictionaryKeyCallBacks,
331 &kCFTypeDictionaryValueCallBacks);
332
333 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
334 keyNameUri.c_str(),
335 kCFStringEncodingUTF8);
336
337 CFDictionaryAddValue(attrDict, kSecAttrKeyType, m_impl->getSymKeyType(keyType));
338 CFDictionaryAddValue(attrDict, kSecAttrKeySizeInBits, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &keySize));
339 CFDictionaryAddValue(attrDict, kSecAttrIsPermanent, kCFBooleanTrue);
340 CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
341
342 CFErrorRef error = NULL;
343
344 SecKeyRef symmetricKey = SecKeyGenerateSymmetric(attrDict, &error);
345
346 if (error)
347 throw Error("Fail to create a symmetric key");
348}
349
350ptr_lib::shared_ptr<PublicKey>
351SecTpmOsx::getPublicKeyFromTpm(const Name & keyName)
352{
353 _LOG_TRACE("OSXPrivateKeyStorage::getPublickey");
354
355 SecKeychainItemRef publicKey = m_impl->getKey(keyName, KEY_CLASS_PUBLIC);
356
357 CFDataRef exportedKey;
358
359 OSStatus res = SecItemExport(publicKey,
360 kSecFormatOpenSSL,
361 0,
362 NULL,
363 &exportedKey);
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800364 if (res != errSecSuccess)
365 {
366 throw Error("Cannot export requested public key from OSX Keychain");
367 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800368
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800369 shared_ptr<PublicKey> key = make_shared<PublicKey>(CFDataGetBytePtr(exportedKey), CFDataGetLength(exportedKey));
370 CFRelease(exportedKey);
371 return key;
372}
373
374ConstBufferPtr
Yingdi Yube4150e2014-02-18 13:02:46 -0800375SecTpmOsx::exportPrivateKeyPkcs1FromTpmInternal(const Name& keyName, bool retry)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800376{
377 using namespace CryptoPP;
378
379 SecKeychainItemRef privateKey = m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
380 CFDataRef exportedKey;
381 OSStatus res = SecItemExport(privateKey,
382 kSecFormatOpenSSL,
383 0,
384 NULL,
385 &exportedKey);
386
387 if(res != errSecSuccess)
388 {
Yingdi Yube4150e2014-02-18 13:02:46 -0800389 if(res == errSecAuthFailed && !retry)
390 {
391 unlockTpm(0, 0, false);
392 return exportPrivateKeyPkcs1FromTpmInternal(keyName, true);
393 }
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
469 OSStatus res = SecKeychainItemImport (importedKey,
470 NULL,
471 &externalFormat,
472 &externalType,
473 0,
474 &keyParams,
475 m_impl->m_keyChainRef,
476 &outItems);
477
478 if(res != errSecSuccess)
479 {
Yingdi Yube4150e2014-02-18 13:02:46 -0800480 if(res == errSecAuthFailed && !retry)
481 {
482 unlockTpm(0, 0, false);
483 return importPrivateKeyPkcs1IntoTpmInternal(keyName, buf, size, true);
484 }
485 else
486 return false;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800487 }
488
489 SecKeychainItemRef privateKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems, 0);
490 SecKeychainAttribute attrs[1]; // maximum number of attributes
491 SecKeychainAttributeList attrList = { 0, attrs };
492 string keyUri = keyName.toUri();
493 {
494 attrs[attrList.count].tag = kSecKeyPrintName;
495 attrs[attrList.count].length = keyUri.size();
496 attrs[attrList.count].data = (void *)keyUri.c_str();
497 attrList.count++;
498 }
499
500 res = SecKeychainItemModifyAttributesAndData(privateKey,
501 &attrList,
502 0,
503 NULL);
504
505 if(res != errSecSuccess)
506 {
507 return false;
508 }
509
510 CFRelease(importedKey);
511 return true;
512}
513
514bool
515SecTpmOsx::importPublicKeyPkcs1IntoTpm(const Name& keyName, const uint8_t* buf, size_t size)
516{
517 CFDataRef importedKey = CFDataCreateWithBytesNoCopy(NULL,
518 buf,
519 size,
520 kCFAllocatorNull);
521
522 SecExternalFormat externalFormat = kSecFormatOpenSSL;
523 SecExternalItemType externalType = kSecItemTypePublicKey;
524 CFArrayRef outItems;
525
526 OSStatus res = SecItemImport (importedKey,
527 NULL,
528 &externalFormat,
529 &externalType,
530 0,
531 NULL,
532 m_impl->m_keyChainRef,
533 &outItems);
534
535 if(res != errSecSuccess)
536 return false;
537
538 SecKeychainItemRef publicKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems, 0);
539 SecKeychainAttribute attrs[1]; // maximum number of attributes
540 SecKeychainAttributeList attrList = { 0, attrs };
541 string keyUri = keyName.toUri();
542 {
543 attrs[attrList.count].tag = kSecKeyPrintName;
544 attrs[attrList.count].length = keyUri.size();
545 attrs[attrList.count].data = (void *)keyUri.c_str();
546 attrList.count++;
547 }
548
549 res = SecKeychainItemModifyAttributesAndData(publicKey,
550 &attrList,
551 0,
552 NULL);
553
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800554 if(res != errSecSuccess)
555 return false;
556
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800557 CFRelease(importedKey);
558 return true;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800559}
560
561Block
Yingdi Yube4150e2014-02-18 13:02:46 -0800562SecTpmOsx::signInTpmInternal(const uint8_t *data, size_t dataLength, const Name& keyName, DigestAlgorithm digestAlgorithm, bool retry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800563{
564 _LOG_TRACE("OSXPrivateKeyStorage::Sign");
565
566 CFDataRef dataRef = CFDataCreateWithBytesNoCopy(NULL,
567 data,
568 dataLength,
569 kCFAllocatorNull
570 );
571
572 SecKeyRef privateKey = (SecKeyRef)m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
573
574 CFErrorRef error;
575 SecTransformRef signer = SecSignTransformCreate((SecKeyRef)privateKey, &error);
576 if (error) throw Error("Fail to create signer");
577
578 // Set input
579 Boolean set_res = SecTransformSetAttribute(signer,
580 kSecTransformInputAttributeName,
581 dataRef,
582 &error);
583 if (error) throw Error("Fail to configure input of signer");
584
585 // Enable use of padding
586 SecTransformSetAttribute(
587 signer,
588 kSecPaddingKey,
589 kSecPaddingPKCS1Key,
590 &error);
591 if (error) throw Error("Fail to configure digest algorithm of signer");
592
593 // Set padding type
594 set_res = SecTransformSetAttribute(signer,
595 kSecDigestTypeAttribute,
596 m_impl->getDigestAlgorithm(digestAlgorithm),
597 &error);
598 if (error) throw Error("Fail to configure digest algorithm of signer");
599
600 // Set padding attribute
601 long digestSize = m_impl->getDigestSize(digestAlgorithm);
602 set_res = SecTransformSetAttribute(signer,
603 kSecDigestLengthAttribute,
604 CFNumberCreate(NULL, kCFNumberLongType, &digestSize),
605 &error);
606 if (error) throw Error("Fail to configure digest size of signer");
607
608 // Actually sign
609 CFDataRef signature = (CFDataRef) SecTransformExecute(signer, &error);
Yingdi Yube4150e2014-02-18 13:02:46 -0800610 if (error)
611 {
612 if(!retry)
613 {
614 unlockTpm(0, 0, false);
615 return signInTpmInternal(data, dataLength, keyName, digestAlgorithm, true);
616 }
617 else
618 {
619 CFShow(error);
620 throw Error("Fail to sign data");
621 }
622 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800623
624 if (!signature) throw Error("Signature is NULL!\n");
625
626 return Block(Tlv::SignatureValue,
627 ptr_lib::make_shared<Buffer>(CFDataGetBytePtr(signature), CFDataGetLength(signature)));
628}
629
630ConstBufferPtr
Yingdi Yufc40d872014-02-18 12:56:04 -0800631SecTpmOsx::decryptInTpm(const uint8_t* data, size_t dataLength, const Name & keyName, bool sym)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800632{
633 _LOG_TRACE("OSXPrivateKeyStorage::Decrypt");
634
635 KeyClass keyClass;
636 if(sym)
637 keyClass = KEY_CLASS_SYMMETRIC;
638 else
639 keyClass = KEY_CLASS_PRIVATE;
640
641 CFDataRef dataRef = CFDataCreate(NULL,
642 reinterpret_cast<const unsigned char*>(data),
643 dataLength
644 );
645
646 // _LOG_DEBUG("CreateData");
647
648 SecKeyRef decryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
649
650 // _LOG_DEBUG("GetKey");
651
652 CFErrorRef error;
653 SecTransformRef decrypt = SecDecryptTransformCreate(decryptKey, &error);
654 if (error) throw Error("Fail to create decrypt");
655
656 Boolean set_res = SecTransformSetAttribute(decrypt,
657 kSecTransformInputAttributeName,
658 dataRef,
659 &error);
660 if (error) throw Error("Fail to configure decrypt");
661
662 CFDataRef output = (CFDataRef) SecTransformExecute(decrypt, &error);
663 if (error)
664 {
665 CFShow(error);
666 throw Error("Fail to decrypt data");
667 }
668 if (!output) throw Error("Output is NULL!\n");
669
670 return ptr_lib::make_shared<Buffer>(CFDataGetBytePtr(output), CFDataGetLength(output));
671}
672
673bool
674SecTpmOsx::setACL(const Name & keyName, KeyClass keyClass, int acl, const string & appPath)
675{
676 SecKeychainItemRef privateKey = m_impl->getKey(keyName, keyClass);
677
678 SecAccessRef accRef;
679 OSStatus acc_res = SecKeychainItemCopyAccess(privateKey, &accRef);
680
681 CFArrayRef signACL = SecAccessCopyMatchingACLList(accRef,
682 kSecACLAuthorizationSign);
683
684 SecACLRef aclRef = (SecACLRef) CFArrayGetValueAtIndex(signACL, 0);
685
686 CFArrayRef appList;
687 CFStringRef description;
688 SecKeychainPromptSelector promptSelector;
689 OSStatus acl_res = SecACLCopyContents(aclRef,
690 &appList,
691 &description,
692 &promptSelector);
693
694 CFMutableArrayRef newAppList = CFArrayCreateMutableCopy(NULL,
695 0,
696 appList);
697
698 SecTrustedApplicationRef trustedApp;
699 acl_res = SecTrustedApplicationCreateFromPath(appPath.c_str(),
700 &trustedApp);
701
702 CFArrayAppendValue(newAppList, trustedApp);
703
704
705 CFArrayRef authList = SecACLCopyAuthorizations(aclRef);
706
707 acl_res = SecACLRemove(aclRef);
708
709 SecACLRef newACL;
710 acl_res = SecACLCreateWithSimpleContents(accRef,
711 newAppList,
712 description,
713 promptSelector,
714 &newACL);
715
716 acl_res = SecACLUpdateAuthorizations(newACL, authList);
717
718 acc_res = SecKeychainItemSetAccess(privateKey, accRef);
719
720 return true;
721}
722
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800723ConstBufferPtr
Yingdi Yufc40d872014-02-18 12:56:04 -0800724SecTpmOsx::encryptInTpm(const uint8_t* data, size_t dataLength, const Name & keyName, bool sym)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800725{
726 _LOG_TRACE("OSXPrivateKeyStorage::Encrypt");
727
728 KeyClass keyClass;
729 if(sym)
730 keyClass = KEY_CLASS_SYMMETRIC;
731 else
732 keyClass = KEY_CLASS_PUBLIC;
733
734 CFDataRef dataRef = CFDataCreate(NULL,
735 reinterpret_cast<const unsigned char*>(data),
736 dataLength
737 );
738
739 SecKeyRef encryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
740
741 CFErrorRef error;
742 SecTransformRef encrypt = SecEncryptTransformCreate(encryptKey, &error);
743 if (error) throw Error("Fail to create encrypt");
744
745 Boolean set_res = SecTransformSetAttribute(encrypt,
746 kSecTransformInputAttributeName,
747 dataRef,
748 &error);
749 if (error) throw Error("Fail to configure encrypt");
750
751 CFDataRef output = (CFDataRef) SecTransformExecute(encrypt, &error);
752 if (error) throw Error("Fail to encrypt data");
753
754 if (!output) throw Error("Output is NULL!\n");
755
756 return ptr_lib::make_shared<Buffer> (CFDataGetBytePtr(output), CFDataGetLength(output));
757}
758
759bool
760SecTpmOsx::doesKeyExistInTpm(const Name & keyName, KeyClass keyClass)
761{
762 _LOG_TRACE("OSXPrivateKeyStorage::doesKeyExist");
763
764 string keyNameUri = m_impl->toInternalKeyName(keyName, keyClass);
765
766 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
767 keyNameUri.c_str(),
768 kCFStringEncodingUTF8);
769
770 CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL,
771 4,
772 &kCFTypeDictionaryKeyCallBacks,
773 NULL);
774
775 CFDictionaryAddValue(attrDict, kSecClass, kSecClassKey);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800776 // CFDictionaryAddValue(attrDict, kSecAttrKeyClass, m_impl->getKeyClass(keyClass));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800777 CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
778 CFDictionaryAddValue(attrDict, kSecReturnRef, kCFBooleanTrue);
779
780 SecKeychainItemRef itemRef;
781 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict, (CFTypeRef*)&itemRef);
782
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800783 if(res == errSecSuccess)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800784 return true;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800785 else
786 return false;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800787
788}
789
Yingdi Yu4b752752014-02-18 12:24:03 -0800790bool
791SecTpmOsx::generateRandomBlock(uint8_t* res, size_t size)
792{
793 return (SecRandomCopyBytes(kSecRandomDefault, size, res) == 0);
794}
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800795
796////////////////////////////////
797// OSXPrivateKeyStorage::Impl //
798////////////////////////////////
799
800SecKeychainItemRef
801SecTpmOsx::Impl::getKey(const Name & keyName, KeyClass keyClass)
802{
803 string keyNameUri = toInternalKeyName(keyName, keyClass);
804
805 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
806 keyNameUri.c_str(),
807 kCFStringEncodingUTF8);
808
809 CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL,
810 5,
811 &kCFTypeDictionaryKeyCallBacks,
812 NULL);
813
814 CFDictionaryAddValue(attrDict, kSecClass, kSecClassKey);
815 CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
816 CFDictionaryAddValue(attrDict, kSecAttrKeyClass, getKeyClass(keyClass));
817 CFDictionaryAddValue(attrDict, kSecReturnRef, kCFBooleanTrue);
818
819 SecKeychainItemRef keyItem;
820
821 OSStatus res = SecItemCopyMatching((CFDictionaryRef) attrDict, (CFTypeRef*)&keyItem);
822
823 if(res != errSecSuccess){
824 _LOG_DEBUG("Fail to find the key!");
825 return NULL;
826 }
827 else
828 return keyItem;
829}
830
831string
832SecTpmOsx::Impl::toInternalKeyName(const Name & keyName, KeyClass keyClass)
833{
834 string keyUri = keyName.toUri();
835
836 if(KEY_CLASS_SYMMETRIC == keyClass)
837 return keyUri + "/symmetric";
838 else
839 return keyUri;
840}
841
842const CFTypeRef
843SecTpmOsx::Impl::getAsymKeyType(KeyType keyType)
844{
845 switch(keyType){
846 case KEY_TYPE_RSA:
847 return kSecAttrKeyTypeRSA;
848 default:
849 _LOG_DEBUG("Unrecognized key type!")
850 return NULL;
851 }
852}
853
854const CFTypeRef
855SecTpmOsx::Impl::getSymKeyType(KeyType keyType)
856{
857 switch(keyType){
858 case KEY_TYPE_AES:
859 return kSecAttrKeyTypeAES;
860 default:
861 _LOG_DEBUG("Unrecognized key type!")
862 return NULL;
863 }
864}
865
866const CFTypeRef
867SecTpmOsx::Impl::getKeyClass(KeyClass keyClass)
868{
869 switch(keyClass){
870 case KEY_CLASS_PRIVATE:
871 return kSecAttrKeyClassPrivate;
872 case KEY_CLASS_PUBLIC:
873 return kSecAttrKeyClassPublic;
874 case KEY_CLASS_SYMMETRIC:
875 return kSecAttrKeyClassSymmetric;
876 default:
877 _LOG_DEBUG("Unrecognized key class!");
878 return NULL;
879 }
880}
881
882const CFStringRef
883SecTpmOsx::Impl::getDigestAlgorithm(DigestAlgorithm digestAlgo)
884{
885 switch(digestAlgo){
Jeff Thompson2747dc02013-10-04 19:11:34 -0700886 // case DIGEST_MD2:
887 // return kSecDigestMD2;
888 // case DIGEST_MD5:
889 // return kSecDigestMD5;
890 // case DIGEST_SHA1:
891 // return kSecDigestSHA1;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800892 case DIGEST_ALGORITHM_SHA256:
893 return kSecDigestSHA2;
894 default:
895 _LOG_DEBUG("Unrecognized digest algorithm!");
896 return NULL;
Jeff Thompson2747dc02013-10-04 19:11:34 -0700897 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800898}
Jeff Thompson2747dc02013-10-04 19:11:34 -0700899
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800900long
901SecTpmOsx::Impl::getDigestSize(DigestAlgorithm digestAlgo)
902{
903 switch(digestAlgo){
904 case DIGEST_ALGORITHM_SHA256:
905 return 256;
Jeff Thompson2747dc02013-10-04 19:11:34 -0700906 // case DIGEST_SHA1:
907 // case DIGEST_MD2:
908 // case DIGEST_MD5:
909 // return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800910 default:
911 _LOG_DEBUG("Unrecognized digest algorithm! Unknown digest size");
912 return -1;
Jeff Thompson2747dc02013-10-04 19:11:34 -0700913 }
Jeff Thompson2747dc02013-10-04 19:11:34 -0700914}
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800915
Yingdi Yufc40d872014-02-18 12:56:04 -0800916} // namespace ndn