NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.5: NDN, CCN, CCNx, content centric networks
API Documentation
sec-tpm-osx.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
24 #include "sec-tpm-osx.hpp"
25 #include "public-key.hpp"
26 
27 #include "../encoding/oid.hpp"
28 #include "../encoding/buffer-stream.hpp"
29 #include "cryptopp.hpp"
30 
31 #include <pwd.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <string.h>
35 
36 #include <boost/lexical_cast.hpp>
37 
38 #include <CoreFoundation/CoreFoundation.h>
39 #include <Security/Security.h>
40 #include <Security/SecRandom.h>
41 #include <CoreServices/CoreServices.h>
42 
43 #include <Security/SecDigestTransform.h>
44 
45 namespace ndn {
46 
47 using std::string;
48 
49 const std::string SecTpmOsx::SCHEME("tpm-osxkeychain");
50 
60 template<class T>
62 {
63 public:
65  // Construction/destruction //
66 
68  : m_typeRef(0)
69  {
70  }
71 
72  CFReleaser(const T& typeRef)
73  : m_typeRef(typeRef)
74  {
75  }
76 
77  CFReleaser(const CFReleaser& inReleaser)
78  : m_typeRef(0)
79  {
80  retain(inReleaser.m_typeRef);
81  }
82 
83  CFReleaser&
84  operator=(const T& typeRef)
85  {
86  if (typeRef != m_typeRef) {
87  release();
88  m_typeRef = typeRef;
89  }
90  return *this;
91  }
92 
93  CFReleaser&
94  operator=(const CFReleaser& inReleaser)
95  {
96  retain(inReleaser.m_typeRef);
97  return *this;
98  }
99 
101  {
102  release();
103  }
104 
106  // Access //
107 
108  // operator const T&() const
109  // {
110  // return m_typeRef;
111  // }
112 
113  // operator T&()
114  // {
115  // return m_typeRef;
116  // }
117 
118  const T&
119  get() const
120  {
121  return m_typeRef;
122  }
123 
124  T&
125  get()
126  {
127  return m_typeRef;
128  }
129 
131  // Miscellaneous //
132 
133  void
134  retain(const T& typeRef)
135  {
136  if (typeRef != 0) {
137  CFRetain(typeRef);
138  }
139  release();
140  m_typeRef = typeRef;
141  }
142 
143  void release()
144  {
145  if (m_typeRef != 0) {
146  CFRelease(m_typeRef);
147  m_typeRef = 0;
148  }
149  };
150 
151 private:
152  T m_typeRef;
153 };
154 
155 
157 {
158 public:
160  : m_passwordSet(false)
161  , m_inTerminal(false)
162  {
163  }
164 
172  std::string
173  toInternalKeyName(const Name& keyName, KeyClass keyClass);
174 
183  getKey(const Name& keyName, KeyClass keyClass);
184 
191  CFTypeRef
192  getSymKeyType(KeyType keyType);
193 
200  CFTypeRef
201  getAsymKeyType(KeyType keyType);
202 
209  CFTypeRef
210  getKeyClass(KeyClass keyClass);
211 
218  CFStringRef
220 
227  long
228  getDigestSize(DigestAlgorithm digestAlgo);
229 
231  // everything here is public, including data //
233 public:
234  SecKeychainRef m_keyChainRef;
236  string m_password;
238 };
239 
240 SecTpmOsx::SecTpmOsx(const std::string& location)
241  : SecTpm(location)
242  , m_impl(new Impl)
243 {
244  // TODO: add location support
245  if (m_impl->m_inTerminal)
246  SecKeychainSetUserInteractionAllowed(false);
247  else
248  SecKeychainSetUserInteractionAllowed(true);
249 
250  OSStatus res = SecKeychainCopyDefault(&m_impl->m_keyChainRef);
251 
252  if (res == errSecNoDefaultKeychain) //If no default key chain, create one.
253  BOOST_THROW_EXCEPTION(Error("No default keychain, please create one first"));
254 }
255 
257 {
258 }
259 
260 void
261 SecTpmOsx::setTpmPassword(const uint8_t* password, size_t passwordLength)
262 {
263  m_impl->m_passwordSet = true;
264  std::fill(m_impl->m_password.begin(), m_impl->m_password.end(), 0);
265  m_impl->m_password.clear();
266  m_impl->m_password.append(reinterpret_cast<const char*>(password), passwordLength);
267 }
268 
269 void
271 {
272  m_impl->m_passwordSet = false;
273  std::fill(m_impl->m_password.begin(), m_impl->m_password.end(), 0);
274  m_impl->m_password.clear();
275 }
276 
277 void
278 SecTpmOsx::setInTerminal(bool inTerminal)
279 {
280  m_impl->m_inTerminal = inTerminal;
281  if (inTerminal)
282  SecKeychainSetUserInteractionAllowed(false);
283  else
284  SecKeychainSetUserInteractionAllowed(true);
285 }
286 
287 bool
289 {
290  return m_impl->m_inTerminal;
291 }
292 
293 bool
295 {
296  SecKeychainStatus keychainStatus;
297 
298  OSStatus res = SecKeychainGetStatus(m_impl->m_keyChainRef, &keychainStatus);
299  if (res != errSecSuccess)
300  return true;
301  else
302  return ((kSecUnlockStateStatus & keychainStatus) == 0);
303 }
304 
305 bool
306 SecTpmOsx::unlockTpm(const char* password, size_t passwordLength, bool usePassword)
307 {
308  OSStatus res;
309 
310  // If the default key chain is already unlocked, return immediately.
311  if (!isLocked())
312  return true;
313 
314  // If the default key chain is locked, unlock the key chain.
315  if (usePassword)
316  {
317  // Use the supplied password.
318  res = SecKeychainUnlock(m_impl->m_keyChainRef,
319  passwordLength,
320  password,
321  true);
322  }
323  else if (m_impl->m_passwordSet)
324  {
325  // If no password supplied, then use the configured password if exists.
326  SecKeychainUnlock(m_impl->m_keyChainRef,
327  m_impl->m_password.size(),
328  m_impl->m_password.c_str(),
329  true);
330  }
331 #ifdef NDN_CXX_HAVE_GETPASS
332  else if (m_impl->m_inTerminal)
333  {
334  // If no configured password, get password from terminal if inTerminal set.
335  bool isLocked = true;
336  const char* fmt = "Password to unlock the default keychain: ";
337  int count = 0;
338 
339  while (isLocked)
340  {
341  if (count > 2)
342  break;
343 
344  char* getPassword = 0;
345  getPassword = getpass(fmt);
346  count++;
347 
348  if (!getPassword)
349  continue;
350 
351  res = SecKeychainUnlock(m_impl->m_keyChainRef,
352  strlen(getPassword),
353  getPassword,
354  true);
355 
356  memset(getPassword, 0, strlen(getPassword));
357 
358  if (res == errSecSuccess)
359  break;
360  }
361  }
362 #endif // NDN_CXX_HAVE_GETPASS
363  else
364  {
365  // If inTerminal is not set, get the password from GUI.
366  SecKeychainUnlock(m_impl->m_keyChainRef, 0, 0, false);
367  }
368 
369  return !isLocked();
370 }
371 
372 void
374  const KeyParams& params,
375  bool needRetry)
376 {
377 
378  if (doesKeyExistInTpm(keyName, KEY_CLASS_PUBLIC))
379  {
380  BOOST_THROW_EXCEPTION(Error("keyName already exists"));
381  }
382 
383  string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_PUBLIC);
384 
385  CFReleaser<CFStringRef> keyLabel =
386  CFStringCreateWithCString(0,
387  keyNameUri.c_str(),
388  kCFStringEncodingUTF8);
389 
391  CFDictionaryCreateMutable(0,
392  3,
393  &kCFTypeDictionaryKeyCallBacks,
394  0);
395 
396  KeyType keyType = params.getKeyType();
397  uint32_t keySize;
398  switch (keyType)
399  {
400  case KEY_TYPE_RSA:
401  {
402  const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(params);
403  keySize = rsaParams.getKeySize();
404  break;
405  }
406  case KEY_TYPE_ECDSA:
407  {
408  const EcdsaKeyParams& ecdsaParams = static_cast<const EcdsaKeyParams&>(params);
409  keySize = ecdsaParams.getKeySize();
410  break;
411  }
412  default:
413  BOOST_THROW_EXCEPTION(Error("Fail to create a key pair: Unsupported key type"));
414  }
415 
416  CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(0, kCFNumberIntType, &keySize);
417 
418  CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, m_impl->getAsymKeyType(keyType));
419  CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
420  CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
421 
422  CFReleaser<SecKeyRef> publicKey, privateKey;
423  // C-style cast is used as per Apple convention
424  OSStatus res = SecKeyGeneratePair((CFDictionaryRef)attrDict.get(),
425  &publicKey.get(), &privateKey.get());
426 
427  if (res == errSecSuccess)
428  {
429  return;
430  }
431 
432  if (res == errSecAuthFailed && !needRetry)
433  {
434  if (unlockTpm(0, 0, false))
435  generateKeyPairInTpmInternal(keyName, params, true);
436  else
437  BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
438  }
439  else
440  {
441  BOOST_THROW_EXCEPTION(Error("Fail to create a key pair"));
442  }
443 }
444 
445 void
446 SecTpmOsx::deleteKeyPairInTpmInternal(const Name& keyName, bool needRetry)
447 {
448  CFReleaser<CFStringRef> keyLabel =
449  CFStringCreateWithCString(0,
450  keyName.toUri().c_str(),
451  kCFStringEncodingUTF8);
452 
454  CFDictionaryCreateMutable(0, 5,
455  &kCFTypeDictionaryKeyCallBacks,
456  &kCFTypeDictionaryValueCallBacks);
457 
458  CFDictionaryAddValue(searchDict.get(), kSecClass, kSecClassKey);
459  CFDictionaryAddValue(searchDict.get(), kSecAttrLabel, keyLabel.get());
460  CFDictionaryAddValue(searchDict.get(), kSecMatchLimit, kSecMatchLimitAll);
461  OSStatus res = SecItemDelete(searchDict.get());
462 
463  if (res == errSecSuccess)
464  return;
465 
466  if (res == errSecAuthFailed && !needRetry)
467  {
468  if (unlockTpm(0, 0, false))
469  deleteKeyPairInTpmInternal(keyName, true);
470  }
471 }
472 
473 void
475 {
476  BOOST_THROW_EXCEPTION(Error("SecTpmOsx::generateSymmetricKeyInTpm is not supported"));
477  // if (doesKeyExistInTpm(keyName, KEY_CLASS_SYMMETRIC))
478  // throw Error("keyName has existed!");
479 
480  // string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_SYMMETRIC);
481 
482  // CFReleaser<CFMutableDictionaryRef> attrDict =
483  // CFDictionaryCreateMutable(kCFAllocatorDefault,
484  // 0,
485  // &kCFTypeDictionaryKeyCallBacks,
486  // &kCFTypeDictionaryValueCallBacks);
487 
488  // CFReleaser<CFStringRef> keyLabel =
489  // CFStringCreateWithCString(0,
490  // keyNameUri.c_str(),
491  // kCFStringEncodingUTF8);
492 
493  // CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(0, kCFNumberIntType, &keySize);
494 
495  // CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, m_impl->getSymKeyType(keyType));
496  // CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
497  // CFDictionaryAddValue(attrDict.get(), kSecAttrIsPermanent, kCFBooleanTrue);
498  // CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
499 
500  // CFErrorRef error = 0;
501 
502  // SecKeyRef symmetricKey = SecKeyGenerateSymmetric(attrDict, &error);
503 
504  // if (error)
505  // throw Error("Fail to create a symmetric key");
506 }
507 
508 shared_ptr<PublicKey>
510 {
511  CFReleaser<SecKeychainItemRef> publicKey = m_impl->getKey(keyName, KEY_CLASS_PUBLIC);
512  if (publicKey.get() == 0)
513  {
514  BOOST_THROW_EXCEPTION(Error("Requested public key [" + keyName.toUri() + "] does not exist "
515  "in OSX Keychain"));
516  }
517 
518  CFReleaser<CFDataRef> exportedKey;
519  OSStatus res = SecItemExport(publicKey.get(),
520  kSecFormatOpenSSL,
521  0,
522  0,
523  &exportedKey.get());
524  if (res != errSecSuccess)
525  {
526  BOOST_THROW_EXCEPTION(Error("Cannot export requested public key from OSX Keychain"));
527  }
528 
529  shared_ptr<PublicKey> key = make_shared<PublicKey>(CFDataGetBytePtr(exportedKey.get()),
530  CFDataGetLength(exportedKey.get()));
531  return key;
532 }
533 
534 std::string
536 {
537  return SCHEME;
538 }
539 
542 {
543  using namespace CryptoPP;
544 
545  CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
546  if (privateKey.get() == 0)
547  {
549  BOOST_THROW_EXCEPTION(Error("Private key [" + keyName.toUri() + "] does not exist "
550  "in OSX Keychain"));
551  }
552 
553  shared_ptr<PublicKey> publicKey = getPublicKeyFromTpm(keyName);
554 
555  CFReleaser<CFDataRef> exportedKey;
556  OSStatus res = SecItemExport(privateKey.get(),
557  kSecFormatOpenSSL,
558  0,
559  0,
560  &exportedKey.get());
561 
562  if (res != errSecSuccess)
563  {
564  if (res == errSecAuthFailed && !needRetry)
565  {
566  if (unlockTpm(0, 0, false))
567  return exportPrivateKeyPkcs8FromTpmInternal(keyName, true);
568  else
569  return shared_ptr<Buffer>();
570  }
571  else
572  return shared_ptr<Buffer>();
573  }
574 
575  uint32_t version = 0;
576  OID algorithm;
577  bool hasParameters = false;
578  OID algorithmParameter;
579  switch (publicKey->getKeyType()) {
580  case KEY_TYPE_RSA:
581  {
582  algorithm = oid::RSA; // "RSA encryption"
583  hasParameters = false;
584  break;
585  }
586  case KEY_TYPE_ECDSA:
587  {
588  // "ECDSA encryption"
589  StringSource src(publicKey->get().buf(), publicKey->get().size(), true);
590  BERSequenceDecoder subjectPublicKeyInfo(src);
591  {
592  BERSequenceDecoder algorithmInfo(subjectPublicKeyInfo);
593  {
594  algorithm.decode(algorithmInfo);
595  algorithmParameter.decode(algorithmInfo);
596  }
597  }
598  hasParameters = true;
599  break;
600  }
601  default:
602  BOOST_THROW_EXCEPTION(Error("Unsupported key type" +
603  boost::lexical_cast<std::string>(publicKey->getKeyType())));
604  }
605 
606  OBufferStream pkcs8Os;
607  FileSink sink(pkcs8Os);
608 
609  SecByteBlock rawKeyBits;
610  // PrivateKeyInfo ::= SEQUENCE {
611  // version INTEGER,
612  // privateKeyAlgorithm SEQUENCE,
613  // privateKey OCTECT STRING}
614  DERSequenceEncoder privateKeyInfo(sink);
615  {
616  DEREncodeUnsigned<uint32_t>(privateKeyInfo, version, INTEGER);
617  DERSequenceEncoder privateKeyAlgorithm(privateKeyInfo);
618  {
619  algorithm.encode(privateKeyAlgorithm);
620  if (hasParameters)
621  algorithmParameter.encode(privateKeyAlgorithm);
622  else
623  DEREncodeNull(privateKeyAlgorithm);
624  }
625  privateKeyAlgorithm.MessageEnd();
626  DEREncodeOctetString(privateKeyInfo,
627  CFDataGetBytePtr(exportedKey.get()),
628  CFDataGetLength(exportedKey.get()));
629  }
630  privateKeyInfo.MessageEnd();
631 
632  return pkcs8Os.buf();
633 }
634 
635 #ifdef __GNUC__
636 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
637 #pragma GCC diagnostic push
638 #endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
639 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
640 #endif // __GNUC__
641 
642 bool
644  const uint8_t* buf, size_t size,
645  bool needRetry)
646 {
647  using namespace CryptoPP;
648 
649  StringSource privateKeySource(buf, size, true);
650  SecByteBlock rawKeyBits;
651  // PrivateKeyInfo ::= SEQUENCE {
652  // INTEGER,
653  // SEQUENCE,
654  // OCTECT STRING}
655  BERSequenceDecoder privateKeyInfo(privateKeySource);
656  {
657  uint32_t versionNum;
658  BERDecodeUnsigned<uint32_t>(privateKeyInfo, versionNum, INTEGER);
659  BERSequenceDecoder sequenceDecoder(privateKeyInfo);
660  {
661  OID keyTypeOID;
662  keyTypeOID.decode(sequenceDecoder);
663 
664  if (keyTypeOID == oid::RSA)
665  BERDecodeNull(sequenceDecoder);
666  else if (keyTypeOID == oid::ECDSA)
667  {
668  OID parameterOID;
669  parameterOID.decode(sequenceDecoder);
670  }
671  else
672  return false; // Unsupported key type;
673 
674 
675  }
676  BERDecodeOctetString(privateKeyInfo, rawKeyBits);
677  }
678  privateKeyInfo.MessageEnd();
679 
680  CFReleaser<CFDataRef> importedKey = CFDataCreateWithBytesNoCopy(0,
681  rawKeyBits.BytePtr(),
682  rawKeyBits.size(),
683  kCFAllocatorNull);
684 
685  SecExternalFormat externalFormat = kSecFormatOpenSSL;
686  SecExternalItemType externalType = kSecItemTypePrivateKey;
687  SecKeyImportExportParameters keyParams;
688  memset(&keyParams, 0, sizeof(keyParams));
689  keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
690  keyParams.keyAttributes = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT;
692  CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
693  keyName.toUri().c_str(),
694  kCFStringEncodingUTF8);
695  SecAccessCreate(keyLabel.get(), 0, &access.get());
696  keyParams.accessRef = access.get();
697  CFReleaser<CFArrayRef> outItems;
698 
699 #ifdef __clang__
700 #pragma clang diagnostic push
701 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
702 #endif // __clang__
703 
704  OSStatus res = SecKeychainItemImport(importedKey.get(),
705  0,
706  &externalFormat,
707  &externalType,
708  0,
709  &keyParams,
710  m_impl->m_keyChainRef,
711  &outItems.get());
712 
713 #ifdef __clang__
714 #pragma clang diagnostic pop
715 #endif // __clang__
716 
717  if (res != errSecSuccess)
718  {
719  if (res == errSecAuthFailed && !needRetry)
720  {
721  if (unlockTpm(0, 0, false))
722  return importPrivateKeyPkcs8IntoTpmInternal(keyName, buf, size, true);
723  else
724  return false;
725  }
726  else
727  return false;
728  }
729 
730  // C-style cast is used as per Apple convention
731  SecKeychainItemRef privateKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
732  SecKeychainAttribute attrs[1]; // maximum number of attributes
733  SecKeychainAttributeList attrList = { 0, attrs };
734  string keyUri = keyName.toUri();
735  {
736  attrs[attrList.count].tag = kSecKeyPrintName;
737  attrs[attrList.count].length = keyUri.size();
738  attrs[attrList.count].data = const_cast<char*>(keyUri.c_str());
739  attrList.count++;
740  }
741 
742  res = SecKeychainItemModifyAttributesAndData(privateKey,
743  &attrList,
744  0,
745  0);
746 
747  if (res != errSecSuccess)
748  {
749  return false;
750  }
751 
752  return true;
753 }
754 
755 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
756 #pragma GCC diagnostic pop
757 #endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
758 
759 bool
760 SecTpmOsx::importPublicKeyPkcs1IntoTpm(const Name& keyName, const uint8_t* buf, size_t size)
761 {
762  CFReleaser<CFDataRef> importedKey = CFDataCreateWithBytesNoCopy(0,
763  buf,
764  size,
765  kCFAllocatorNull);
766 
767  SecExternalFormat externalFormat = kSecFormatOpenSSL;
768  SecExternalItemType externalType = kSecItemTypePublicKey;
769  CFReleaser<CFArrayRef> outItems;
770 
771  OSStatus res = SecItemImport(importedKey.get(),
772  0,
773  &externalFormat,
774  &externalType,
775  0,
776  0,
777  m_impl->m_keyChainRef,
778  &outItems.get());
779 
780  if (res != errSecSuccess)
781  return false;
782 
783  // C-style cast is used as per Apple convention
784  SecKeychainItemRef publicKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
785  SecKeychainAttribute attrs[1]; // maximum number of attributes
786  SecKeychainAttributeList attrList = { 0, attrs };
787  string keyUri = keyName.toUri();
788  {
789  attrs[attrList.count].tag = kSecKeyPrintName;
790  attrs[attrList.count].length = keyUri.size();
791  attrs[attrList.count].data = const_cast<char*>(keyUri.c_str());
792  attrList.count++;
793  }
794 
795  res = SecKeychainItemModifyAttributesAndData(publicKey,
796  &attrList,
797  0,
798  0);
799 
800  if (res != errSecSuccess)
801  return false;
802 
803  return true;
804 }
805 
806 Block
807 SecTpmOsx::signInTpmInternal(const uint8_t* data, size_t dataLength,
808  const Name& keyName, DigestAlgorithm digestAlgorithm, bool needRetry)
809 {
810  CFReleaser<CFDataRef> dataRef = CFDataCreateWithBytesNoCopy(0,
811  data,
812  dataLength,
813  kCFAllocatorNull);
814 
815  CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
816  if (privateKey.get() == 0)
817  {
818  BOOST_THROW_EXCEPTION(Error("Private key [" + keyName.toUri() + "] does not exist "
819  "in OSX Keychain"));
820  }
821 
823  // C-style cast is used as per Apple convention
824  CFReleaser<SecTransformRef> signer = SecSignTransformCreate((SecKeyRef)privateKey.get(),
825  &error.get());
826  if (error.get() != 0)
827  BOOST_THROW_EXCEPTION(Error("Fail to create signer"));
828 
829  // Set input
830  SecTransformSetAttribute(signer.get(),
831  kSecTransformInputAttributeName,
832  dataRef.get(),
833  &error.get());
834  if (error.get() != 0)
835  BOOST_THROW_EXCEPTION(Error("Fail to configure input of signer"));
836 
837  // Enable use of padding
838  SecTransformSetAttribute(signer.get(),
839  kSecPaddingKey,
840  kSecPaddingPKCS1Key,
841  &error.get());
842  if (error.get() != 0)
843  BOOST_THROW_EXCEPTION(Error("Fail to configure digest algorithm of signer"));
844 
845  // Set padding type
846  SecTransformSetAttribute(signer.get(),
847  kSecDigestTypeAttribute,
848  m_impl->getDigestAlgorithm(digestAlgorithm),
849  &error.get());
850  if (error.get() != 0)
851  BOOST_THROW_EXCEPTION(Error("Fail to configure digest algorithm of signer"));
852 
853  // Set padding attribute
854  long digestSize = m_impl->getDigestSize(digestAlgorithm);
855  CFReleaser<CFNumberRef> cfDigestSize = CFNumberCreate(0, kCFNumberLongType, &digestSize);
856  SecTransformSetAttribute(signer.get(),
857  kSecDigestLengthAttribute,
858  cfDigestSize.get(),
859  &error.get());
860  if (error.get() != 0)
861  BOOST_THROW_EXCEPTION(Error("Fail to configure digest size of signer"));
862 
863  // Actually sign
864  // C-style cast is used as per Apple convention
865  CFReleaser<CFDataRef> signature = (CFDataRef)SecTransformExecute(signer.get(), &error.get());
866  if (error.get() != 0)
867  {
868  if (!needRetry)
869  {
870  if (unlockTpm(0, 0, false))
871  return signInTpmInternal(data, dataLength, keyName, digestAlgorithm, true);
872  else
873  BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
874  }
875  else
876  {
877  CFShow(error.get());
878  BOOST_THROW_EXCEPTION(Error("Fail to sign data"));
879  }
880  }
881 
882  if (signature.get() == 0)
883  BOOST_THROW_EXCEPTION(Error("Signature is NULL!\n"));
884 
885  return Block(tlv::SignatureValue,
886  make_shared<Buffer>(CFDataGetBytePtr(signature.get()),
887  CFDataGetLength(signature.get())));
888 }
889 
891 SecTpmOsx::decryptInTpm(const uint8_t* data, size_t dataLength, const Name& keyName, bool sym)
892 {
893  BOOST_THROW_EXCEPTION(Error("SecTpmOsx::decryptInTpm is not supported"));
894 
895  // KeyClass keyClass;
896  // if (sym)
897  // keyClass = KEY_CLASS_SYMMETRIC;
898  // else
899  // keyClass = KEY_CLASS_PRIVATE;
900 
901  // CFDataRef dataRef = CFDataCreate(0,
902  // reinterpret_cast<const unsigned char*>(data),
903  // dataLength
904  // );
905 
906  // CFReleaser<SecKeyRef> decryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
907  // if (decryptKey.get() == 0)
908  // {
909  // /// @todo Can this happen because of keychain is locked?
910  // throw Error("Decruption key [" + ??? + "] does not exist in OSX Keychain");
911  // }
912 
913  // CFErrorRef error;
914  // SecTransformRef decrypt = SecDecryptTransformCreate(decryptKey, &error);
915  // if (error) throw Error("Fail to create decrypt");
916 
917  // Boolean set_res = SecTransformSetAttribute(decrypt,
918  // kSecTransformInputAttributeName,
919  // dataRef,
920  // &error);
921  // if (error) throw Error("Fail to configure decrypt");
922 
923  // CFDataRef output = (CFDataRef) SecTransformExecute(decrypt, &error);
924  // if (error)
925  // {
926  // CFShow(error);
927  // throw Error("Fail to decrypt data");
928  // }
929  // if (!output) throw Error("Output is NULL!\n");
930 
931  // return make_shared<Buffer>(CFDataGetBytePtr(output), CFDataGetLength(output));
932 }
933 
934 void
935 SecTpmOsx::addAppToAcl(const Name& keyName, KeyClass keyClass, const string& appPath, AclType acl)
936 {
937  if (keyClass == KEY_CLASS_PRIVATE && acl == ACL_TYPE_PRIVATE)
938  {
939  CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, keyClass);
940  if (privateKey.get() == 0)
941  {
942  BOOST_THROW_EXCEPTION(Error("Private key [" + keyName.toUri() + "] does not exist "
943  "in OSX Keychain"));
944  }
945 
947  SecKeychainItemCopyAccess(privateKey.get(), &accRef.get());
948 
949  CFReleaser<CFArrayRef> signACL = SecAccessCopyMatchingACLList(accRef.get(),
950  kSecACLAuthorizationSign);
951 
952  // C-style cast is used as per Apple convention
953  SecACLRef aclRef = (SecACLRef)CFArrayGetValueAtIndex(signACL.get(), 0);
954 
955  CFReleaser<CFArrayRef> appList;
956  CFReleaser<CFStringRef> description;
957  SecKeychainPromptSelector promptSelector;
958  SecACLCopyContents(aclRef,
959  &appList.get(),
960  &description.get(),
961  &promptSelector);
962 
963  CFReleaser<CFMutableArrayRef> newAppList = CFArrayCreateMutableCopy(0,
964  0,
965  appList.get());
966 
968  SecTrustedApplicationCreateFromPath(appPath.c_str(),
969  &trustedApp.get());
970 
971  CFArrayAppendValue(newAppList.get(), trustedApp.get());
972 
973  SecACLSetContents(aclRef,
974  newAppList.get(),
975  description.get(),
976  promptSelector);
977 
978  SecKeychainItemSetAccess(privateKey.get(), accRef.get());
979  }
980 }
981 
983 SecTpmOsx::encryptInTpm(const uint8_t* data, size_t dataLength, const Name& keyName, bool sym)
984 {
985  BOOST_THROW_EXCEPTION(Error("SecTpmOsx::encryptInTpm is not supported"));
986 
987  // KeyClass keyClass;
988  // if (sym)
989  // keyClass = KEY_CLASS_SYMMETRIC;
990  // else
991  // keyClass = KEY_CLASS_PUBLIC;
992 
993  // CFDataRef dataRef = CFDataCreate(0,
994  // reinterpret_cast<const unsigned char*>(data),
995  // dataLength
996  // );
997 
998  // CFReleaser<SecKeyRef> encryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
999  // if (encryptKey.get() == 0)
1000  // {
1001  // throw Error("Encryption key [" + ???? + "] does not exist in OSX Keychain");
1002  // }
1003 
1004  // CFErrorRef error;
1005  // SecTransformRef encrypt = SecEncryptTransformCreate(encryptKey, &error);
1006  // if (error) throw Error("Fail to create encrypt");
1007 
1008  // Boolean set_res = SecTransformSetAttribute(encrypt,
1009  // kSecTransformInputAttributeName,
1010  // dataRef,
1011  // &error);
1012  // if (error) throw Error("Fail to configure encrypt");
1013 
1014  // CFDataRef output = (CFDataRef) SecTransformExecute(encrypt, &error);
1015  // if (error) throw Error("Fail to encrypt data");
1016 
1017  // if (!output) throw Error("Output is NULL!\n");
1018 
1019  // return make_shared<Buffer> (CFDataGetBytePtr(output), CFDataGetLength(output));
1020 }
1021 
1022 bool
1023 SecTpmOsx::doesKeyExistInTpm(const Name& keyName, KeyClass keyClass)
1024 {
1025  string keyNameUri = m_impl->toInternalKeyName(keyName, keyClass);
1026 
1027  CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
1028  keyNameUri.c_str(),
1029  kCFStringEncodingUTF8);
1030 
1032  CFDictionaryCreateMutable(0,
1033  4,
1034  &kCFTypeDictionaryKeyCallBacks,
1035  0);
1036 
1037  CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
1038  // CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, m_impl->getKeyClass(keyClass));
1039  CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
1040  CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
1041 
1043  // C-style cast is used as per Apple convention
1044  OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&itemRef.get());
1045 
1046  if (res == errSecSuccess)
1047  return true;
1048  else
1049  return false;
1050 
1051 }
1052 
1053 bool
1054 SecTpmOsx::generateRandomBlock(uint8_t* res, size_t size)
1055 {
1056  return SecRandomCopyBytes(kSecRandomDefault, size, res) == 0;
1057 }
1058 
1060 // OSXPrivateKeyStorage::Impl //
1062 
1064 SecTpmOsx::Impl::getKey(const Name& keyName, KeyClass keyClass)
1065 {
1066  string keyNameUri = toInternalKeyName(keyName, keyClass);
1067 
1068  CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
1069  keyNameUri.c_str(),
1070  kCFStringEncodingUTF8);
1071 
1073  CFDictionaryCreateMutable(0,
1074  5,
1075  &kCFTypeDictionaryKeyCallBacks,
1076  0);
1077 
1078  CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
1079  CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
1080  CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, getKeyClass(keyClass));
1081  CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
1082 
1084  // C-style cast is used as per Apple convention
1085  OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&keyItem.get());
1086 
1087  if (res != errSecSuccess)
1088  return 0;
1089  else
1090  return keyItem;
1091 }
1092 
1093 string
1095 {
1096  string keyUri = keyName.toUri();
1097 
1098  if (KEY_CLASS_SYMMETRIC == keyClass)
1099  return keyUri + "/symmetric";
1100  else
1101  return keyUri;
1102 }
1103 
1104 CFTypeRef
1106 {
1107  switch (keyType) {
1108  case KEY_TYPE_RSA:
1109  return kSecAttrKeyTypeRSA;
1110  case KEY_TYPE_ECDSA:
1111  return kSecAttrKeyTypeECDSA;
1112  default:
1113  return 0;
1114  }
1115 }
1116 
1117 CFTypeRef
1119 {
1120  switch (keyType) {
1121  case KEY_TYPE_AES:
1122  return kSecAttrKeyTypeAES;
1123  default:
1124  return 0;
1125  }
1126 }
1127 
1128 CFTypeRef
1130 {
1131  switch (keyClass) {
1132  case KEY_CLASS_PRIVATE:
1133  return kSecAttrKeyClassPrivate;
1134  case KEY_CLASS_PUBLIC:
1135  return kSecAttrKeyClassPublic;
1136  case KEY_CLASS_SYMMETRIC:
1137  return kSecAttrKeyClassSymmetric;
1138  default:
1139  return 0;
1140  }
1141 }
1142 
1143 CFStringRef
1145 {
1146  switch (digestAlgo) {
1148  return kSecDigestSHA2;
1149  default:
1150  return 0;
1151  }
1152 }
1153 
1154 long
1156 {
1157  switch (digestAlgo) {
1159  return 256;
1160  default:
1161  return -1;
1162  }
1163 }
1164 
1165 } // namespace ndn
void deleteKeyPairInTpmInternal(const Name &keyName, bool needRetry)
Copyright (c) 2011-2015 Regents of the University of California.
static const std::string SCHEME
Helper class to wrap CoreFoundation object pointers.
Definition: sec-tpm-osx.cpp:61
std::string toUri() const
Encode this name as a URI.
Definition: name.cpp:183
Copyright (c) 2013-2014 Regents of the University of California.
Definition: oid.hpp:29
const T & get() const
virtual ConstBufferPtr encryptInTpm(const uint8_t *data, size_t dataLength, const Name &keyName, bool isSymmetric)
Encrypt data.
CFReleaser & operator=(const CFReleaser &inReleaser)
Definition: sec-tpm-osx.cpp:94
SecTpmOsx(const std::string &location="")
virtual bool importPublicKeyPkcs1IntoTpm(const Name &keyName, const uint8_t *buf, size_t size)
Import a public key in PKCS#1 formatted.
void encode(CryptoPP::BufferedTransformation &out) const
Definition: oid.cpp:130
virtual bool getInTerminal() const
Get value of inTerminal flag.
CFTypeRef getSymKeyType(KeyType keyType)
Convert keyType to MAC OS symmetric key key type.
Class representing a wire element of NDN-TLV packet format.
Definition: block.hpp:43
ConstBufferPtr exportPrivateKeyPkcs8FromTpmInternal(const Name &keyName, bool needRetry)
CFReleaser(const T &typeRef)
Definition: sec-tpm-osx.cpp:72
CFReleaser & operator=(const T &typeRef)
Definition: sec-tpm-osx.cpp:84
virtual void setTpmPassword(const uint8_t *password, size_t passwordLength)
set password of TPM
virtual ~SecTpmOsx()
virtual void resetTpmPassword()
reset password of TPM
shared_ptr< Buffer > buf()
Flush written data to the stream and return shared pointer to the underlying buffer.
void decode(CryptoPP::BufferedTransformation &in)
Definition: oid.cpp:147
void retain(const T &typeRef)
CFReleaser< SecKeychainItemRef > getKey(const Name &keyName, KeyClass keyClass)
Get key.
virtual void generateSymmetricKeyInTpm(const Name &keyName, const KeyParams &params)
Generate a symmetric key.
CFStringRef getDigestAlgorithm(DigestAlgorithm digestAlgo)
Convert digestAlgo to MAC OS algorithm id.
virtual shared_ptr< PublicKey > getPublicKeyFromTpm(const Name &keyName)
Get a public key.
SecTpm is the base class of the TPM classes.
Definition: sec-tpm.hpp:41
CFTypeRef getKeyClass(KeyClass keyClass)
Convert keyClass to MAC OS key class.
bool importPrivateKeyPkcs8IntoTpmInternal(const Name &keyName, const uint8_t *buf, size_t size, bool needRetry)
virtual void addAppToAcl(const Name &keyName, KeyClass keyClass, const std::string &appPath, AclType acl)
Add the application into the ACL of a particular key.
Name abstraction to represent an absolute name.
Definition: name.hpp:46
KeyType getKeyType() const
Definition: key-params.hpp:54
Definition: oid.hpp:35
virtual ConstBufferPtr decryptInTpm(const uint8_t *data, size_t dataLength, const Name &keyName, bool isSymmetric)
Decrypt data.
void generateKeyPairInTpmInternal(const Name &keyName, const KeyParams &params, bool needRetry)
std::string toInternalKeyName(const Name &keyName, KeyClass keyClass)
Convert NDN name of a key to internal name of the key.
CFReleaser(const CFReleaser &inReleaser)
Definition: sec-tpm-osx.cpp:77
virtual bool doesKeyExistInTpm(const Name &keyName, KeyClass keyClass)
Check if a particular key exists.
Base class of key parameters.
Definition: key-params.hpp:35
uint32_t getKeySize() const
Definition: key-params.hpp:135
virtual void setInTerminal(bool inTerminal)
Set inTerminal flag to.
long getDigestSize(DigestAlgorithm digestAlgo)
Get the digest size of the corresponding algorithm.
Class implementing interface similar to ostringstream, but to construct ndn::Buffer.
shared_ptr< const Buffer > ConstBufferPtr
Definition: buffer.hpp:33
const OID ECDSA("1.2.840.10045.2.1")
Definition: oid.hpp:102
SimplePublicKeyParams is a template for public keys with only one parameter: size.
Definition: key-params.hpp:110
Block signInTpmInternal(const uint8_t *data, size_t dataLength, const Name &keyName, DigestAlgorithm digestAlgorithm, bool needRetry)
SecKeychainRef m_keyChainRef
virtual std::string getScheme()
const OID RSA("1.2.840.113549.1.1.1")
Definition: oid.hpp:101
virtual bool generateRandomBlock(uint8_t *res, size_t size)
Generate a random block.
virtual bool unlockTpm(const char *password, size_t passwordLength, bool usePassword)
Unlock the TPM.
virtual bool isLocked()
Check if TPM is locked.
CFTypeRef getAsymKeyType(KeyType keyType)
Convert keyType to MAC OS asymmetirc key type.