NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.0: 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 
170  std::string
171  toInternalKeyName(const Name& keyName, KeyClass keyClass);
172 
179  getKey(const Name& keyName, KeyClass keyClass);
180 
186  CFTypeRef
187  getSymKeyType(KeyType keyType);
188 
194  CFTypeRef
195  getAsymKeyType(KeyType keyType);
196 
202  CFTypeRef
203  getKeyClass(KeyClass keyClass);
204 
210  CFStringRef
211  getDigestAlgorithm(DigestAlgorithm digestAlgo);
212 
218  long
219  getDigestSize(DigestAlgorithm digestAlgo);
220 
222  // everything here is public, including data //
224 public:
225  SecKeychainRef m_keyChainRef;
227  string m_password;
229 };
230 
231 SecTpmOsx::SecTpmOsx(const std::string& location)
232  : SecTpm(location)
233  , m_impl(new Impl)
234 {
235  // TODO: add location support
236  if (m_impl->m_inTerminal)
237  SecKeychainSetUserInteractionAllowed(false);
238  else
239  SecKeychainSetUserInteractionAllowed(true);
240 
241  OSStatus res = SecKeychainCopyDefault(&m_impl->m_keyChainRef);
242 
243  if (res == errSecNoDefaultKeychain) //If no default key chain, create one.
244  BOOST_THROW_EXCEPTION(Error("No default keychain, please create one first"));
245 }
246 
248 {
249 }
250 
251 void
252 SecTpmOsx::setTpmPassword(const uint8_t* password, size_t passwordLength)
253 {
254  m_impl->m_passwordSet = true;
255  std::fill(m_impl->m_password.begin(), m_impl->m_password.end(), 0);
256  m_impl->m_password.clear();
257  m_impl->m_password.append(reinterpret_cast<const char*>(password), passwordLength);
258 }
259 
260 void
262 {
263  m_impl->m_passwordSet = false;
264  std::fill(m_impl->m_password.begin(), m_impl->m_password.end(), 0);
265  m_impl->m_password.clear();
266 }
267 
268 void
269 SecTpmOsx::setInTerminal(bool inTerminal)
270 {
271  m_impl->m_inTerminal = inTerminal;
272  if (inTerminal)
273  SecKeychainSetUserInteractionAllowed(false);
274  else
275  SecKeychainSetUserInteractionAllowed(true);
276 }
277 
278 bool
280 {
281  return m_impl->m_inTerminal;
282 }
283 
284 bool
286 {
287  SecKeychainStatus keychainStatus;
288 
289  OSStatus res = SecKeychainGetStatus(m_impl->m_keyChainRef, &keychainStatus);
290  if (res != errSecSuccess)
291  return true;
292  else
293  return ((kSecUnlockStateStatus & keychainStatus) == 0);
294 }
295 
296 bool
297 SecTpmOsx::unlockTpm(const char* password, size_t passwordLength, bool usePassword)
298 {
299  OSStatus res;
300 
301  // If the default key chain is already unlocked, return immediately.
302  if (!isLocked())
303  return true;
304 
305  // If the default key chain is locked, unlock the key chain.
306  if (usePassword)
307  {
308  // Use the supplied password.
309  res = SecKeychainUnlock(m_impl->m_keyChainRef,
310  passwordLength,
311  password,
312  true);
313  }
314  else if (m_impl->m_passwordSet)
315  {
316  // If no password supplied, then use the configured password if exists.
317  SecKeychainUnlock(m_impl->m_keyChainRef,
318  m_impl->m_password.size(),
319  m_impl->m_password.c_str(),
320  true);
321  }
322 #ifdef NDN_CXX_HAVE_GETPASS
323  else if (m_impl->m_inTerminal)
324  {
325  // If no configured password, get password from terminal if inTerminal set.
326  bool isLocked = true;
327  const char* fmt = "Password to unlock the default keychain: ";
328  int count = 0;
329 
330  while (isLocked)
331  {
332  if (count > 2)
333  break;
334 
335  char* getPassword = 0;
336  getPassword = getpass(fmt);
337  count++;
338 
339  if (!getPassword)
340  continue;
341 
342  res = SecKeychainUnlock(m_impl->m_keyChainRef,
343  strlen(getPassword),
344  getPassword,
345  true);
346 
347  memset(getPassword, 0, strlen(getPassword));
348 
349  if (res == errSecSuccess)
350  break;
351  }
352  }
353 #endif // NDN_CXX_HAVE_GETPASS
354  else
355  {
356  // If inTerminal is not set, get the password from GUI.
357  SecKeychainUnlock(m_impl->m_keyChainRef, 0, 0, false);
358  }
359 
360  return !isLocked();
361 }
362 
363 void
365  const KeyParams& params,
366  bool needRetry)
367 {
368 
369  if (doesKeyExistInTpm(keyName, KEY_CLASS_PUBLIC))
370  {
371  BOOST_THROW_EXCEPTION(Error("keyName already exists"));
372  }
373 
374  string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_PUBLIC);
375 
376  CFReleaser<CFStringRef> keyLabel =
377  CFStringCreateWithCString(0,
378  keyNameUri.c_str(),
379  kCFStringEncodingUTF8);
380 
382  CFDictionaryCreateMutable(0,
383  3,
384  &kCFTypeDictionaryKeyCallBacks,
385  0);
386 
387  KeyType keyType = params.getKeyType();
388  uint32_t keySize;
389  switch (keyType)
390  {
391  case KEY_TYPE_RSA:
392  {
393  const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(params);
394  keySize = rsaParams.getKeySize();
395  break;
396  }
397  case KEY_TYPE_ECDSA:
398  {
399  const EcdsaKeyParams& ecdsaParams = static_cast<const EcdsaKeyParams&>(params);
400  keySize = ecdsaParams.getKeySize();
401  break;
402  }
403  default:
404  BOOST_THROW_EXCEPTION(Error("Fail to create a key pair: Unsupported key type"));
405  }
406 
407  CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(0, kCFNumberIntType, &keySize);
408 
409  CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, m_impl->getAsymKeyType(keyType));
410  CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
411  CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
412 
413  CFReleaser<SecKeyRef> publicKey, privateKey;
414  // C-style cast is used as per Apple convention
415  OSStatus res = SecKeyGeneratePair((CFDictionaryRef)attrDict.get(),
416  &publicKey.get(), &privateKey.get());
417 
418  if (res == errSecSuccess)
419  {
420  return;
421  }
422 
423  if (res == errSecAuthFailed && !needRetry)
424  {
425  if (unlockTpm(0, 0, false))
426  generateKeyPairInTpmInternal(keyName, params, true);
427  else
428  BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
429  }
430  else
431  {
432  BOOST_THROW_EXCEPTION(Error("Fail to create a key pair"));
433  }
434 }
435 
436 void
437 SecTpmOsx::deleteKeyPairInTpmInternal(const Name& keyName, bool needRetry)
438 {
439  CFReleaser<CFStringRef> keyLabel =
440  CFStringCreateWithCString(0,
441  keyName.toUri().c_str(),
442  kCFStringEncodingUTF8);
443 
445  CFDictionaryCreateMutable(0, 5,
446  &kCFTypeDictionaryKeyCallBacks,
447  &kCFTypeDictionaryValueCallBacks);
448 
449  CFDictionaryAddValue(searchDict.get(), kSecClass, kSecClassKey);
450  CFDictionaryAddValue(searchDict.get(), kSecAttrLabel, keyLabel.get());
451  CFDictionaryAddValue(searchDict.get(), kSecMatchLimit, kSecMatchLimitAll);
452  OSStatus res = SecItemDelete(searchDict.get());
453 
454  if (res == errSecSuccess)
455  return;
456 
457  if (res == errSecAuthFailed && !needRetry)
458  {
459  if (unlockTpm(0, 0, false))
460  deleteKeyPairInTpmInternal(keyName, true);
461  }
462 }
463 
464 void
466 {
467  BOOST_THROW_EXCEPTION(Error("SecTpmOsx::generateSymmetricKeyInTpm is not supported"));
468  // if (doesKeyExistInTpm(keyName, KEY_CLASS_SYMMETRIC))
469  // throw Error("keyName has existed!");
470 
471  // string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_SYMMETRIC);
472 
473  // CFReleaser<CFMutableDictionaryRef> attrDict =
474  // CFDictionaryCreateMutable(kCFAllocatorDefault,
475  // 0,
476  // &kCFTypeDictionaryKeyCallBacks,
477  // &kCFTypeDictionaryValueCallBacks);
478 
479  // CFReleaser<CFStringRef> keyLabel =
480  // CFStringCreateWithCString(0,
481  // keyNameUri.c_str(),
482  // kCFStringEncodingUTF8);
483 
484  // CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(0, kCFNumberIntType, &keySize);
485 
486  // CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, m_impl->getSymKeyType(keyType));
487  // CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
488  // CFDictionaryAddValue(attrDict.get(), kSecAttrIsPermanent, kCFBooleanTrue);
489  // CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
490 
491  // CFErrorRef error = 0;
492 
493  // SecKeyRef symmetricKey = SecKeyGenerateSymmetric(attrDict, &error);
494 
495  // if (error)
496  // throw Error("Fail to create a symmetric key");
497 }
498 
499 shared_ptr<PublicKey>
501 {
502  CFReleaser<SecKeychainItemRef> publicKey = m_impl->getKey(keyName, KEY_CLASS_PUBLIC);
503  if (publicKey.get() == 0)
504  {
505  BOOST_THROW_EXCEPTION(Error("Requested public key [" + keyName.toUri() + "] does not exist "
506  "in OSX Keychain"));
507  }
508 
509  CFReleaser<CFDataRef> exportedKey;
510  OSStatus res = SecItemExport(publicKey.get(),
511  kSecFormatOpenSSL,
512  0,
513  0,
514  &exportedKey.get());
515  if (res != errSecSuccess)
516  {
517  BOOST_THROW_EXCEPTION(Error("Cannot export requested public key from OSX Keychain"));
518  }
519 
520  shared_ptr<PublicKey> key = make_shared<PublicKey>(CFDataGetBytePtr(exportedKey.get()),
521  CFDataGetLength(exportedKey.get()));
522  return key;
523 }
524 
525 std::string
527 {
528  return SCHEME;
529 }
530 
533 {
534  using namespace CryptoPP;
535 
536  CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
537  if (privateKey.get() == 0)
538  {
540  BOOST_THROW_EXCEPTION(Error("Private key [" + keyName.toUri() + "] does not exist "
541  "in OSX Keychain"));
542  }
543 
544  shared_ptr<PublicKey> publicKey = getPublicKeyFromTpm(keyName);
545 
546  CFReleaser<CFDataRef> exportedKey;
547  OSStatus res = SecItemExport(privateKey.get(),
548  kSecFormatOpenSSL,
549  0,
550  0,
551  &exportedKey.get());
552 
553  if (res != errSecSuccess)
554  {
555  if (res == errSecAuthFailed && !needRetry)
556  {
557  if (unlockTpm(0, 0, false))
558  return exportPrivateKeyPkcs8FromTpmInternal(keyName, true);
559  else
560  return shared_ptr<Buffer>();
561  }
562  else
563  return shared_ptr<Buffer>();
564  }
565 
566  uint32_t version = 0;
567  OID algorithm;
568  bool hasParameters = false;
569  OID algorithmParameter;
570  switch (publicKey->getKeyType()) {
571  case KEY_TYPE_RSA:
572  {
573  algorithm = oid::RSA; // "RSA encryption"
574  hasParameters = false;
575  break;
576  }
577  case KEY_TYPE_ECDSA:
578  {
579  // "ECDSA encryption"
580  StringSource src(publicKey->get().buf(), publicKey->get().size(), true);
581  BERSequenceDecoder subjectPublicKeyInfo(src);
582  {
583  BERSequenceDecoder algorithmInfo(subjectPublicKeyInfo);
584  {
585  algorithm.decode(algorithmInfo);
586  algorithmParameter.decode(algorithmInfo);
587  }
588  }
589  hasParameters = true;
590  break;
591  }
592  default:
593  BOOST_THROW_EXCEPTION(Error("Unsupported key type" +
594  boost::lexical_cast<std::string>(publicKey->getKeyType())));
595  }
596 
597  OBufferStream pkcs8Os;
598  FileSink sink(pkcs8Os);
599 
600  SecByteBlock rawKeyBits;
601  // PrivateKeyInfo ::= SEQUENCE {
602  // version INTEGER,
603  // privateKeyAlgorithm SEQUENCE,
604  // privateKey OCTECT STRING}
605  DERSequenceEncoder privateKeyInfo(sink);
606  {
607  DEREncodeUnsigned<uint32_t>(privateKeyInfo, version, INTEGER);
608  DERSequenceEncoder privateKeyAlgorithm(privateKeyInfo);
609  {
610  algorithm.encode(privateKeyAlgorithm);
611  if (hasParameters)
612  algorithmParameter.encode(privateKeyAlgorithm);
613  else
614  DEREncodeNull(privateKeyAlgorithm);
615  }
616  privateKeyAlgorithm.MessageEnd();
617  DEREncodeOctetString(privateKeyInfo,
618  CFDataGetBytePtr(exportedKey.get()),
619  CFDataGetLength(exportedKey.get()));
620  }
621  privateKeyInfo.MessageEnd();
622 
623  return pkcs8Os.buf();
624 }
625 
626 #ifdef __GNUC__
627 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
628 #pragma GCC diagnostic push
629 #endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
630 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
631 #endif // __GNUC__
632 
633 bool
635  const uint8_t* buf, size_t size,
636  bool needRetry)
637 {
638  using namespace CryptoPP;
639 
640  StringSource privateKeySource(buf, size, true);
641  SecByteBlock rawKeyBits;
642  // PrivateKeyInfo ::= SEQUENCE {
643  // INTEGER,
644  // SEQUENCE,
645  // OCTECT STRING}
646  BERSequenceDecoder privateKeyInfo(privateKeySource);
647  {
648  uint32_t versionNum;
649  BERDecodeUnsigned<uint32_t>(privateKeyInfo, versionNum, INTEGER);
650  BERSequenceDecoder sequenceDecoder(privateKeyInfo);
651  {
652  OID keyTypeOID;
653  keyTypeOID.decode(sequenceDecoder);
654 
655  if (keyTypeOID == oid::RSA)
656  BERDecodeNull(sequenceDecoder);
657  else if (keyTypeOID == oid::ECDSA)
658  {
659  OID parameterOID;
660  parameterOID.decode(sequenceDecoder);
661  }
662  else
663  return false; // Unsupported key type;
664 
665 
666  }
667  BERDecodeOctetString(privateKeyInfo, rawKeyBits);
668  }
669  privateKeyInfo.MessageEnd();
670 
671  CFReleaser<CFDataRef> importedKey = CFDataCreateWithBytesNoCopy(0,
672  rawKeyBits.BytePtr(),
673  rawKeyBits.size(),
674  kCFAllocatorNull);
675 
676  SecExternalFormat externalFormat = kSecFormatOpenSSL;
677  SecExternalItemType externalType = kSecItemTypePrivateKey;
678  SecKeyImportExportParameters keyParams;
679  memset(&keyParams, 0, sizeof(keyParams));
680  keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
681  keyParams.keyAttributes = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT;
683  CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
684  keyName.toUri().c_str(),
685  kCFStringEncodingUTF8);
686  SecAccessCreate(keyLabel.get(), 0, &access.get());
687  keyParams.accessRef = access.get();
688  CFReleaser<CFArrayRef> outItems;
689 
690 #ifdef __clang__
691 #pragma clang diagnostic push
692 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
693 #endif // __clang__
694 
695  OSStatus res = SecKeychainItemImport(importedKey.get(),
696  0,
697  &externalFormat,
698  &externalType,
699  0,
700  &keyParams,
701  m_impl->m_keyChainRef,
702  &outItems.get());
703 
704 #ifdef __clang__
705 #pragma clang diagnostic pop
706 #endif // __clang__
707 
708  if (res != errSecSuccess)
709  {
710  if (res == errSecAuthFailed && !needRetry)
711  {
712  if (unlockTpm(0, 0, false))
713  return importPrivateKeyPkcs8IntoTpmInternal(keyName, buf, size, true);
714  else
715  return false;
716  }
717  else
718  return false;
719  }
720 
721  // C-style cast is used as per Apple convention
722  SecKeychainItemRef privateKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
723  SecKeychainAttribute attrs[1]; // maximum number of attributes
724  SecKeychainAttributeList attrList = { 0, attrs };
725  string keyUri = keyName.toUri();
726  {
727  attrs[attrList.count].tag = kSecKeyPrintName;
728  attrs[attrList.count].length = keyUri.size();
729  attrs[attrList.count].data = const_cast<char*>(keyUri.c_str());
730  attrList.count++;
731  }
732 
733  res = SecKeychainItemModifyAttributesAndData(privateKey,
734  &attrList,
735  0,
736  0);
737 
738  if (res != errSecSuccess)
739  {
740  return false;
741  }
742 
743  return true;
744 }
745 
746 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
747 #pragma GCC diagnostic pop
748 #endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
749 
750 bool
751 SecTpmOsx::importPublicKeyPkcs1IntoTpm(const Name& keyName, const uint8_t* buf, size_t size)
752 {
753  CFReleaser<CFDataRef> importedKey = CFDataCreateWithBytesNoCopy(0,
754  buf,
755  size,
756  kCFAllocatorNull);
757 
758  SecExternalFormat externalFormat = kSecFormatOpenSSL;
759  SecExternalItemType externalType = kSecItemTypePublicKey;
760  CFReleaser<CFArrayRef> outItems;
761 
762  OSStatus res = SecItemImport(importedKey.get(),
763  0,
764  &externalFormat,
765  &externalType,
766  0,
767  0,
768  m_impl->m_keyChainRef,
769  &outItems.get());
770 
771  if (res != errSecSuccess)
772  return false;
773 
774  // C-style cast is used as per Apple convention
775  SecKeychainItemRef publicKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
776  SecKeychainAttribute attrs[1]; // maximum number of attributes
777  SecKeychainAttributeList attrList = { 0, attrs };
778  string keyUri = keyName.toUri();
779  {
780  attrs[attrList.count].tag = kSecKeyPrintName;
781  attrs[attrList.count].length = keyUri.size();
782  attrs[attrList.count].data = const_cast<char*>(keyUri.c_str());
783  attrList.count++;
784  }
785 
786  res = SecKeychainItemModifyAttributesAndData(publicKey,
787  &attrList,
788  0,
789  0);
790 
791  if (res != errSecSuccess)
792  return false;
793 
794  return true;
795 }
796 
797 Block
798 SecTpmOsx::signInTpmInternal(const uint8_t* data, size_t dataLength,
799  const Name& keyName, DigestAlgorithm digestAlgorithm, bool needRetry)
800 {
801  CFReleaser<CFDataRef> dataRef = CFDataCreateWithBytesNoCopy(0,
802  data,
803  dataLength,
804  kCFAllocatorNull);
805 
806  CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
807  if (privateKey.get() == 0)
808  {
809  BOOST_THROW_EXCEPTION(Error("Private key [" + keyName.toUri() + "] does not exist "
810  "in OSX Keychain"));
811  }
812 
814  // C-style cast is used as per Apple convention
815  CFReleaser<SecTransformRef> signer = SecSignTransformCreate((SecKeyRef)privateKey.get(),
816  &error.get());
817  if (error.get() != 0)
818  BOOST_THROW_EXCEPTION(Error("Fail to create signer"));
819 
820  // Set input
821  SecTransformSetAttribute(signer.get(),
822  kSecTransformInputAttributeName,
823  dataRef.get(),
824  &error.get());
825  if (error.get() != 0)
826  BOOST_THROW_EXCEPTION(Error("Fail to configure input of signer"));
827 
828  // Enable use of padding
829  SecTransformSetAttribute(signer.get(),
830  kSecPaddingKey,
831  kSecPaddingPKCS1Key,
832  &error.get());
833  if (error.get() != 0)
834  BOOST_THROW_EXCEPTION(Error("Fail to configure digest algorithm of signer"));
835 
836  // Set padding type
837  SecTransformSetAttribute(signer.get(),
838  kSecDigestTypeAttribute,
839  m_impl->getDigestAlgorithm(digestAlgorithm),
840  &error.get());
841  if (error.get() != 0)
842  BOOST_THROW_EXCEPTION(Error("Fail to configure digest algorithm of signer"));
843 
844  // Set padding attribute
845  long digestSize = m_impl->getDigestSize(digestAlgorithm);
846  CFReleaser<CFNumberRef> cfDigestSize = CFNumberCreate(0, kCFNumberLongType, &digestSize);
847  SecTransformSetAttribute(signer.get(),
848  kSecDigestLengthAttribute,
849  cfDigestSize.get(),
850  &error.get());
851  if (error.get() != 0)
852  BOOST_THROW_EXCEPTION(Error("Fail to configure digest size of signer"));
853 
854  // Actually sign
855  // C-style cast is used as per Apple convention
856  CFReleaser<CFDataRef> signature = (CFDataRef)SecTransformExecute(signer.get(), &error.get());
857  if (error.get() != 0)
858  {
859  if (!needRetry)
860  {
861  if (unlockTpm(0, 0, false))
862  return signInTpmInternal(data, dataLength, keyName, digestAlgorithm, true);
863  else
864  BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
865  }
866  else
867  {
868  CFShow(error.get());
869  BOOST_THROW_EXCEPTION(Error("Fail to sign data"));
870  }
871  }
872 
873  if (signature.get() == 0)
874  BOOST_THROW_EXCEPTION(Error("Signature is NULL!\n"));
875 
876  return Block(tlv::SignatureValue,
877  make_shared<Buffer>(CFDataGetBytePtr(signature.get()),
878  CFDataGetLength(signature.get())));
879 }
880 
882 SecTpmOsx::decryptInTpm(const uint8_t* data, size_t dataLength, const Name& keyName, bool sym)
883 {
884  BOOST_THROW_EXCEPTION(Error("SecTpmOsx::decryptInTpm is not supported"));
885 
886  // KeyClass keyClass;
887  // if (sym)
888  // keyClass = KEY_CLASS_SYMMETRIC;
889  // else
890  // keyClass = KEY_CLASS_PRIVATE;
891 
892  // CFDataRef dataRef = CFDataCreate(0,
893  // reinterpret_cast<const unsigned char*>(data),
894  // dataLength
895  // );
896 
897  // CFReleaser<SecKeyRef> decryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
898  // if (decryptKey.get() == 0)
899  // {
900  // /// @todo Can this happen because of keychain is locked?
901  // throw Error("Decruption key [" + ??? + "] does not exist in OSX Keychain");
902  // }
903 
904  // CFErrorRef error;
905  // SecTransformRef decrypt = SecDecryptTransformCreate(decryptKey, &error);
906  // if (error) throw Error("Fail to create decrypt");
907 
908  // Boolean set_res = SecTransformSetAttribute(decrypt,
909  // kSecTransformInputAttributeName,
910  // dataRef,
911  // &error);
912  // if (error) throw Error("Fail to configure decrypt");
913 
914  // CFDataRef output = (CFDataRef) SecTransformExecute(decrypt, &error);
915  // if (error)
916  // {
917  // CFShow(error);
918  // throw Error("Fail to decrypt data");
919  // }
920  // if (!output) throw Error("Output is NULL!\n");
921 
922  // return make_shared<Buffer>(CFDataGetBytePtr(output), CFDataGetLength(output));
923 }
924 
925 void
926 SecTpmOsx::addAppToAcl(const Name& keyName, KeyClass keyClass, const string& appPath, AclType acl)
927 {
928  if (keyClass == KEY_CLASS_PRIVATE && acl == ACL_TYPE_PRIVATE)
929  {
930  CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, keyClass);
931  if (privateKey.get() == 0)
932  {
933  BOOST_THROW_EXCEPTION(Error("Private key [" + keyName.toUri() + "] does not exist "
934  "in OSX Keychain"));
935  }
936 
938  SecKeychainItemCopyAccess(privateKey.get(), &accRef.get());
939 
940  CFReleaser<CFArrayRef> signACL = SecAccessCopyMatchingACLList(accRef.get(),
941  kSecACLAuthorizationSign);
942 
943  // C-style cast is used as per Apple convention
944  SecACLRef aclRef = (SecACLRef)CFArrayGetValueAtIndex(signACL.get(), 0);
945 
946  CFReleaser<CFArrayRef> appList;
947  CFReleaser<CFStringRef> description;
948  SecKeychainPromptSelector promptSelector;
949  SecACLCopyContents(aclRef,
950  &appList.get(),
951  &description.get(),
952  &promptSelector);
953 
954  CFReleaser<CFMutableArrayRef> newAppList = CFArrayCreateMutableCopy(0,
955  0,
956  appList.get());
957 
959  SecTrustedApplicationCreateFromPath(appPath.c_str(),
960  &trustedApp.get());
961 
962  CFArrayAppendValue(newAppList.get(), trustedApp.get());
963 
964  SecACLSetContents(aclRef,
965  newAppList.get(),
966  description.get(),
967  promptSelector);
968 
969  SecKeychainItemSetAccess(privateKey.get(), accRef.get());
970  }
971 }
972 
974 SecTpmOsx::encryptInTpm(const uint8_t* data, size_t dataLength, const Name& keyName, bool sym)
975 {
976  BOOST_THROW_EXCEPTION(Error("SecTpmOsx::encryptInTpm is not supported"));
977 
978  // KeyClass keyClass;
979  // if (sym)
980  // keyClass = KEY_CLASS_SYMMETRIC;
981  // else
982  // keyClass = KEY_CLASS_PUBLIC;
983 
984  // CFDataRef dataRef = CFDataCreate(0,
985  // reinterpret_cast<const unsigned char*>(data),
986  // dataLength
987  // );
988 
989  // CFReleaser<SecKeyRef> encryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
990  // if (encryptKey.get() == 0)
991  // {
992  // throw Error("Encryption key [" + ???? + "] does not exist in OSX Keychain");
993  // }
994 
995  // CFErrorRef error;
996  // SecTransformRef encrypt = SecEncryptTransformCreate(encryptKey, &error);
997  // if (error) throw Error("Fail to create encrypt");
998 
999  // Boolean set_res = SecTransformSetAttribute(encrypt,
1000  // kSecTransformInputAttributeName,
1001  // dataRef,
1002  // &error);
1003  // if (error) throw Error("Fail to configure encrypt");
1004 
1005  // CFDataRef output = (CFDataRef) SecTransformExecute(encrypt, &error);
1006  // if (error) throw Error("Fail to encrypt data");
1007 
1008  // if (!output) throw Error("Output is NULL!\n");
1009 
1010  // return make_shared<Buffer> (CFDataGetBytePtr(output), CFDataGetLength(output));
1011 }
1012 
1013 bool
1014 SecTpmOsx::doesKeyExistInTpm(const Name& keyName, KeyClass keyClass)
1015 {
1016  string keyNameUri = m_impl->toInternalKeyName(keyName, keyClass);
1017 
1018  CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
1019  keyNameUri.c_str(),
1020  kCFStringEncodingUTF8);
1021 
1023  CFDictionaryCreateMutable(0,
1024  4,
1025  &kCFTypeDictionaryKeyCallBacks,
1026  0);
1027 
1028  CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
1029  // CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, m_impl->getKeyClass(keyClass));
1030  CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
1031  CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
1032 
1034  // C-style cast is used as per Apple convention
1035  OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&itemRef.get());
1036 
1037  if (res == errSecSuccess)
1038  return true;
1039  else
1040  return false;
1041 
1042 }
1043 
1044 bool
1045 SecTpmOsx::generateRandomBlock(uint8_t* res, size_t size)
1046 {
1047  return SecRandomCopyBytes(kSecRandomDefault, size, res) == 0;
1048 }
1049 
1051 // OSXPrivateKeyStorage::Impl //
1053 
1055 SecTpmOsx::Impl::getKey(const Name& keyName, KeyClass keyClass)
1056 {
1057  string keyNameUri = toInternalKeyName(keyName, keyClass);
1058 
1059  CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
1060  keyNameUri.c_str(),
1061  kCFStringEncodingUTF8);
1062 
1064  CFDictionaryCreateMutable(0,
1065  5,
1066  &kCFTypeDictionaryKeyCallBacks,
1067  0);
1068 
1069  CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
1070  CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
1071  CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, getKeyClass(keyClass));
1072  CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
1073 
1075  // C-style cast is used as per Apple convention
1076  OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&keyItem.get());
1077 
1078  if (res != errSecSuccess)
1079  return 0;
1080  else
1081  return keyItem;
1082 }
1083 
1084 string
1086 {
1087  string keyUri = keyName.toUri();
1088 
1089  if (KEY_CLASS_SYMMETRIC == keyClass)
1090  return keyUri + "/symmetric";
1091  else
1092  return keyUri;
1093 }
1094 
1095 CFTypeRef
1097 {
1098  switch (keyType) {
1099  case KEY_TYPE_RSA:
1100  return kSecAttrKeyTypeRSA;
1101  case KEY_TYPE_ECDSA:
1102  return kSecAttrKeyTypeECDSA;
1103  default:
1104  return 0;
1105  }
1106 }
1107 
1108 CFTypeRef
1110 {
1111  switch (keyType) {
1112  case KEY_TYPE_AES:
1113  return kSecAttrKeyTypeAES;
1114  default:
1115  return 0;
1116  }
1117 }
1118 
1119 CFTypeRef
1121 {
1122  switch (keyClass) {
1123  case KEY_CLASS_PRIVATE:
1124  return kSecAttrKeyClassPrivate;
1125  case KEY_CLASS_PUBLIC:
1126  return kSecAttrKeyClassPublic;
1127  case KEY_CLASS_SYMMETRIC:
1128  return kSecAttrKeyClassSymmetric;
1129  default:
1130  return 0;
1131  }
1132 }
1133 
1134 CFStringRef
1136 {
1137  switch (digestAlgo) {
1139  return kSecDigestSHA2;
1140  default:
1141  return 0;
1142  }
1143 }
1144 
1145 long
1147 {
1148  switch (digestAlgo) {
1150  return 256;
1151  default:
1152  return -1;
1153  }
1154 }
1155 
1156 } // 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 buffer of size bufferSize.
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
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 inTerminal.
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.