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