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