security: Move KeyChain to security::v1 namespace and deprecated it

Change-Id: Ic4b6915ca15998a83b410f3f8fac027f797ee7ca
Refs: #3098
diff --git a/src/security/v1/sec-tpm-osx.cpp b/src/security/v1/sec-tpm-osx.cpp
new file mode 100644
index 0000000..f3c3029
--- /dev/null
+++ b/src/security/v1/sec-tpm-osx.cpp
@@ -0,0 +1,1145 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
+ */
+
+#include "sec-tpm-osx.hpp"
+#include "public-key.hpp"
+
+#include "../../encoding/oid.hpp"
+#include "../../encoding/buffer-stream.hpp"
+#include "cryptopp.hpp"
+
+#include <pwd.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+#include <Security/SecRandom.h>
+#include <CoreServices/CoreServices.h>
+
+#include <Security/SecDigestTransform.h>
+
+namespace ndn {
+namespace security {
+namespace v1 {
+
+using std::string;
+
+const std::string SecTpmOsx::SCHEME("tpm-osxkeychain");
+
+/**
+ * @brief Helper class to wrap CoreFoundation object pointers
+ *
+ * The class is similar in spirit to shared_ptr, but uses CoreFoundation
+ * mechanisms to retain/release object.
+ *
+ * Original implementation by Christopher Hunt and it was borrowed from
+ * http://www.cocoabuilder.com/archive/cocoa/130776-auto-cfrelease-and.html
+ */
+template<class T>
+class CFReleaser
+{
+public:
+  //////////////////////////////
+  // Construction/destruction //
+
+  CFReleaser()
+    : m_typeRef(nullptr)
+  {
+  }
+
+  CFReleaser(const T& typeRef)
+    : m_typeRef(typeRef)
+  {
+  }
+
+  CFReleaser(const CFReleaser& inReleaser)
+    : m_typeRef(nullptr)
+  {
+    retain(inReleaser.m_typeRef);
+  }
+
+  CFReleaser&
+  operator=(const T& typeRef)
+  {
+    if (typeRef != m_typeRef) {
+      release();
+      m_typeRef = typeRef;
+    }
+    return *this;
+  }
+
+  CFReleaser&
+  operator=(const CFReleaser& inReleaser)
+  {
+    retain(inReleaser.m_typeRef);
+    return *this;
+  }
+
+  ~CFReleaser()
+  {
+    release();
+  }
+
+  ////////////
+  // Access //
+
+  // operator const T&() const
+  // {
+  //   return m_typeRef;
+  // }
+
+  // operator T&()
+  // {
+  //   return m_typeRef;
+  // }
+
+  const T&
+  get() const
+  {
+    return m_typeRef;
+  }
+
+  T&
+  get()
+  {
+    return m_typeRef;
+  }
+
+  ///////////////////
+  // Miscellaneous //
+
+  void
+  retain(const T& typeRef)
+  {
+    if (typeRef != nullptr) {
+      CFRetain(typeRef);
+    }
+    release();
+    m_typeRef = typeRef;
+  }
+
+  void
+  release()
+  {
+    if (m_typeRef != nullptr) {
+      CFRelease(m_typeRef);
+      m_typeRef = nullptr;
+    }
+  };
+
+  bool
+  operator==(std::nullptr_t)
+  {
+    return get() == nullptr;
+  }
+
+  bool
+  operator!=(std::nullptr_t)
+  {
+    return get() != nullptr;
+  }
+
+private:
+  T m_typeRef;
+};
+
+
+class SecTpmOsx::Impl
+{
+public:
+  Impl()
+    : m_passwordSet(false)
+    , m_inTerminal(false)
+  {
+  }
+
+  /**
+   * @brief Convert NDN name of a key to internal name of the key.
+   *
+   * @return the internal key name
+   */
+  std::string
+  toInternalKeyName(const Name& keyName, KeyClass keyClass);
+
+  /**
+   * @brief Get key.
+   *
+   * @returns pointer to the key
+   */
+  CFReleaser<SecKeychainItemRef>
+  getKey(const Name& keyName, KeyClass keyClass);
+
+  /**
+   * @brief Convert keyType to MAC OS symmetric key key type
+   *
+   * @returns MAC OS key type
+   */
+  CFTypeRef
+  getSymKeyType(KeyType keyType);
+
+  /**
+   * @brief Convert keyType to MAC OS asymmetirc key type
+   *
+   * @returns MAC OS key type
+   */
+  CFTypeRef
+  getAsymKeyType(KeyType keyType);
+
+  /**
+   * @brief Convert keyClass to MAC OS key class
+   *
+   * @returns MAC OS key class
+   */
+  CFTypeRef
+  getKeyClass(KeyClass keyClass);
+
+  /**
+   * @brief Convert digestAlgo to MAC OS algorithm id
+   *
+   * @returns MAC OS algorithm id
+   */
+  CFStringRef
+  getDigestAlgorithm(DigestAlgorithm digestAlgo);
+
+  /**
+   * @brief Get the digest size of the corresponding algorithm
+   *
+   * @return digest size
+   */
+  long
+  getDigestSize(DigestAlgorithm digestAlgo);
+
+  ///////////////////////////////////////////////
+  // everything here is public, including data //
+  ///////////////////////////////////////////////
+public:
+  SecKeychainRef m_keyChainRef;
+  bool m_passwordSet;
+  string m_password;
+  bool m_inTerminal;
+};
+
+SecTpmOsx::SecTpmOsx(const std::string& location)
+  : SecTpm(location)
+  , m_impl(new Impl)
+{
+  // TODO: add location support
+  if (m_impl->m_inTerminal)
+    SecKeychainSetUserInteractionAllowed(false);
+  else
+    SecKeychainSetUserInteractionAllowed(true);
+
+  OSStatus res = SecKeychainCopyDefault(&m_impl->m_keyChainRef);
+
+  if (res == errSecNoDefaultKeychain) //If no default key chain, create one.
+    BOOST_THROW_EXCEPTION(Error("No default keychain, please create one first"));
+}
+
+SecTpmOsx::~SecTpmOsx()
+{
+}
+
+void
+SecTpmOsx::setTpmPassword(const uint8_t* password, size_t passwordLength)
+{
+  m_impl->m_passwordSet = true;
+  std::fill(m_impl->m_password.begin(), m_impl->m_password.end(), 0);
+  m_impl->m_password.clear();
+  m_impl->m_password.append(reinterpret_cast<const char*>(password), passwordLength);
+}
+
+void
+SecTpmOsx::resetTpmPassword()
+{
+  m_impl->m_passwordSet = false;
+  std::fill(m_impl->m_password.begin(), m_impl->m_password.end(), 0);
+  m_impl->m_password.clear();
+}
+
+void
+SecTpmOsx::setInTerminal(bool inTerminal)
+{
+  m_impl->m_inTerminal = inTerminal;
+  if (inTerminal)
+    SecKeychainSetUserInteractionAllowed(false);
+  else
+    SecKeychainSetUserInteractionAllowed(true);
+}
+
+bool
+SecTpmOsx::getInTerminal() const
+{
+  return m_impl->m_inTerminal;
+}
+
+bool
+SecTpmOsx::isLocked()
+{
+  SecKeychainStatus keychainStatus;
+
+  OSStatus res = SecKeychainGetStatus(m_impl->m_keyChainRef, &keychainStatus);
+  if (res != errSecSuccess)
+    return true;
+  else
+    return ((kSecUnlockStateStatus & keychainStatus) == 0);
+}
+
+bool
+SecTpmOsx::unlockTpm(const char* password, size_t passwordLength, bool usePassword)
+{
+  OSStatus res;
+
+  // If the default key chain is already unlocked, return immediately.
+  if (!isLocked())
+    return true;
+
+  // If the default key chain is locked, unlock the key chain.
+  if (usePassword) {
+    // Use the supplied password.
+    res = SecKeychainUnlock(m_impl->m_keyChainRef,
+                            passwordLength,
+                            password,
+                            true);
+  }
+  else if (m_impl->m_passwordSet) {
+    // If no password supplied, then use the configured password if exists.
+    SecKeychainUnlock(m_impl->m_keyChainRef,
+                      m_impl->m_password.size(),
+                      m_impl->m_password.c_str(),
+                      true);
+  }
+#ifdef NDN_CXX_HAVE_GETPASS
+  else if (m_impl->m_inTerminal) {
+    // If no configured password, get password from terminal if inTerminal set.
+    bool isLocked = true;
+    const char* fmt = "Password to unlock the default keychain: ";
+    int count = 0;
+
+    while (isLocked) {
+      if (count > 2)
+        break;
+
+      char* getPassword = nullptr;
+      getPassword = getpass(fmt);
+      count++;
+
+      if (!getPassword)
+        continue;
+
+      res = SecKeychainUnlock(m_impl->m_keyChainRef,
+                              strlen(getPassword),
+                              getPassword,
+                              true);
+
+      memset(getPassword, 0, strlen(getPassword));
+
+      if (res == errSecSuccess)
+        break;
+    }
+  }
+#endif // NDN_CXX_HAVE_GETPASS
+  else {
+    // If inTerminal is not set, get the password from GUI.
+    SecKeychainUnlock(m_impl->m_keyChainRef, 0, nullptr, false);
+  }
+
+  return !isLocked();
+}
+
+void
+SecTpmOsx::generateKeyPairInTpmInternal(const Name& keyName,
+                                        const KeyParams& params,
+                                        bool needRetry)
+{
+
+  if (doesKeyExistInTpm(keyName, KeyClass::PUBLIC)) {
+    BOOST_THROW_EXCEPTION(Error("keyName already exists"));
+  }
+
+  string keyNameUri = m_impl->toInternalKeyName(keyName, KeyClass::PUBLIC);
+
+  CFReleaser<CFStringRef> keyLabel =
+    CFStringCreateWithCString(0,
+                              keyNameUri.c_str(),
+                              kCFStringEncodingUTF8);
+
+  CFReleaser<CFMutableDictionaryRef> attrDict =
+    CFDictionaryCreateMutable(0,
+                              3,
+                              &kCFTypeDictionaryKeyCallBacks,
+                              0);
+
+  KeyType keyType = params.getKeyType();
+  uint32_t keySize = 0;
+  switch (keyType) {
+    case KeyType::RSA: {
+      const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(params);
+      keySize = rsaParams.getKeySize();
+      break;
+    }
+
+    case KeyType::EC: {
+      const EcdsaKeyParams& ecdsaParams = static_cast<const EcdsaKeyParams&>(params);
+      keySize = ecdsaParams.getKeySize();
+      break;
+    }
+
+    default:
+      BOOST_THROW_EXCEPTION(Error("Fail to create a key pair: Unsupported key type"));
+  }
+
+  CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(0, kCFNumberIntType, &keySize);
+
+  CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, m_impl->getAsymKeyType(keyType));
+  CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
+  CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
+
+  CFReleaser<SecKeyRef> publicKey, privateKey;
+  // C-style cast is used as per Apple convention
+  OSStatus res = SecKeyGeneratePair((CFDictionaryRef)attrDict.get(),
+                                    &publicKey.get(), &privateKey.get());
+
+  if (res == errSecSuccess) {
+    return;
+  }
+
+  if (res == errSecAuthFailed && !needRetry) {
+    if (unlockTpm(nullptr, 0, false))
+      generateKeyPairInTpmInternal(keyName, params, true);
+    else
+      BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
+  }
+  else {
+    BOOST_THROW_EXCEPTION(Error("Fail to create a key pair"));
+  }
+}
+
+void
+SecTpmOsx::deleteKeyPairInTpmInternal(const Name& keyName, bool needRetry)
+{
+  CFReleaser<CFStringRef> keyLabel =
+    CFStringCreateWithCString(0,
+                              keyName.toUri().c_str(),
+                              kCFStringEncodingUTF8);
+
+  CFReleaser<CFMutableDictionaryRef> searchDict =
+    CFDictionaryCreateMutable(0, 5,
+                              &kCFTypeDictionaryKeyCallBacks,
+                              &kCFTypeDictionaryValueCallBacks);
+
+  CFDictionaryAddValue(searchDict.get(), kSecClass, kSecClassKey);
+  CFDictionaryAddValue(searchDict.get(), kSecAttrLabel, keyLabel.get());
+  CFDictionaryAddValue(searchDict.get(), kSecMatchLimit, kSecMatchLimitAll);
+  OSStatus res = SecItemDelete(searchDict.get());
+
+  if (res == errSecSuccess)
+    return;
+
+  if (res == errSecAuthFailed && !needRetry) {
+    if (unlockTpm(nullptr, 0, false))
+      deleteKeyPairInTpmInternal(keyName, true);
+  }
+}
+
+void
+SecTpmOsx::generateSymmetricKeyInTpm(const Name& keyName, const KeyParams& params)
+{
+  BOOST_THROW_EXCEPTION(Error("SecTpmOsx::generateSymmetricKeyInTpm is not supported"));
+  // if (doesKeyExistInTpm(keyName, KeyClass::SYMMETRIC))
+  //   throw Error("keyName has existed!");
+
+  // string keyNameUri =  m_impl->toInternalKeyName(keyName, KeyClass::SYMMETRIC);
+
+  // CFReleaser<CFMutableDictionaryRef> attrDict =
+  //   CFDictionaryCreateMutable(kCFAllocatorDefault,
+  //                             0,
+  //                             &kCFTypeDictionaryKeyCallBacks,
+  //                             &kCFTypeDictionaryValueCallBacks);
+
+  // CFReleaser<CFStringRef> keyLabel =
+  //   CFStringCreateWithCString(0,
+  //                             keyNameUri.c_str(),
+  //                             kCFStringEncodingUTF8);
+
+  // CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(0, kCFNumberIntType, &keySize);
+
+  // CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, m_impl->getSymKeyType(keyType));
+  // CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
+  // CFDictionaryAddValue(attrDict.get(), kSecAttrIsPermanent, kCFBooleanTrue);
+  // CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
+
+  // CFErrorRef error = 0;
+
+  // SecKeyRef symmetricKey = SecKeyGenerateSymmetric(attrDict, &error);
+
+  // if (error)
+  //   throw Error("Fail to create a symmetric key");
+}
+
+shared_ptr<PublicKey>
+SecTpmOsx::getPublicKeyFromTpm(const Name& keyName)
+{
+  CFReleaser<SecKeychainItemRef> publicKey = m_impl->getKey(keyName, KeyClass::PUBLIC);
+  if (publicKey == nullptr) {
+    BOOST_THROW_EXCEPTION(Error("Requested public key [" + keyName.toUri() + "] does not exist "
+                                "in OSX Keychain"));
+  }
+
+  CFReleaser<CFDataRef> exportedKey;
+  OSStatus res = SecItemExport(publicKey.get(),
+                               kSecFormatOpenSSL,
+                               0,
+                               nullptr,
+                               &exportedKey.get());
+  if (res != errSecSuccess) {
+    BOOST_THROW_EXCEPTION(Error("Cannot export requested public key from OSX Keychain"));
+  }
+
+  shared_ptr<PublicKey> key = make_shared<PublicKey>(CFDataGetBytePtr(exportedKey.get()),
+                                                             CFDataGetLength(exportedKey.get()));
+  return key;
+}
+
+std::string
+SecTpmOsx::getScheme()
+{
+  return SCHEME;
+}
+
+ConstBufferPtr
+SecTpmOsx::exportPrivateKeyPkcs8FromTpmInternal(const Name& keyName, bool needRetry)
+{
+  using namespace CryptoPP;
+
+  CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, KeyClass::PRIVATE);
+  if (privateKey == nullptr) {
+    /// @todo Can this happen because of keychain is locked?
+    BOOST_THROW_EXCEPTION(Error("Private key [" + keyName.toUri() + "] does not exist "
+                                "in OSX Keychain"));
+  }
+
+  shared_ptr<PublicKey> publicKey = getPublicKeyFromTpm(keyName);
+
+  CFReleaser<CFDataRef> exportedKey;
+  OSStatus res = SecItemExport(privateKey.get(),
+                               kSecFormatOpenSSL,
+                               0,
+                               nullptr,
+                               &exportedKey.get());
+
+  if (res != errSecSuccess) {
+    if (res == errSecAuthFailed && !needRetry) {
+      if (unlockTpm(nullptr, 0, false))
+        return exportPrivateKeyPkcs8FromTpmInternal(keyName, true);
+      else
+        return nullptr;
+    }
+    else
+      return nullptr;
+  }
+
+  uint32_t version = 0;
+  Oid algorithm;
+  bool hasParameters = false;
+  Oid algorithmParameter;
+  switch (publicKey->getKeyType()) {
+    case KeyType::RSA: {
+      algorithm = oid::RSA; // "RSA encryption"
+      hasParameters = false;
+      break;
+    }
+
+    case KeyType::EC: {
+      // "ECDSA encryption"
+      StringSource src(publicKey->get().buf(), publicKey->get().size(), true);
+      BERSequenceDecoder subjectPublicKeyInfo(src);
+      {
+        BERSequenceDecoder algorithmInfo(subjectPublicKeyInfo);
+        {
+          algorithm.decode(algorithmInfo);
+          algorithmParameter.decode(algorithmInfo);
+        }
+      }
+      hasParameters = true;
+      break;
+    }
+
+    default:
+      BOOST_THROW_EXCEPTION(Error("Unsupported key type" +
+                                  boost::lexical_cast<std::string>(publicKey->getKeyType())));
+  }
+
+  OBufferStream pkcs8Os;
+  FileSink sink(pkcs8Os);
+
+  SecByteBlock rawKeyBits;
+  // PrivateKeyInfo ::= SEQUENCE {
+  //   version              INTEGER,
+  //   privateKeyAlgorithm  SEQUENCE,
+  //   privateKey           OCTECT STRING}
+  DERSequenceEncoder privateKeyInfo(sink);
+  {
+    DEREncodeUnsigned<uint32_t>(privateKeyInfo, version, INTEGER);
+    DERSequenceEncoder privateKeyAlgorithm(privateKeyInfo);
+    {
+      algorithm.encode(privateKeyAlgorithm);
+      if (hasParameters)
+        algorithmParameter.encode(privateKeyAlgorithm);
+      else
+        DEREncodeNull(privateKeyAlgorithm);
+    }
+    privateKeyAlgorithm.MessageEnd();
+    DEREncodeOctetString(privateKeyInfo,
+                         CFDataGetBytePtr(exportedKey.get()),
+                         CFDataGetLength(exportedKey.get()));
+  }
+  privateKeyInfo.MessageEnd();
+
+  return pkcs8Os.buf();
+}
+
+#ifdef __GNUC__
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#pragma GCC diagnostic push
+#endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif // __GNUC__
+
+bool
+SecTpmOsx::importPrivateKeyPkcs8IntoTpmInternal(const Name& keyName,
+                                                const uint8_t* buf, size_t size,
+                                                bool needRetry)
+{
+  using namespace CryptoPP;
+
+  StringSource privateKeySource(buf, size, true);
+  SecByteBlock rawKeyBits;
+  // PrivateKeyInfo ::= SEQUENCE {
+  //   INTEGER,
+  //   SEQUENCE,
+  //   OCTECT STRING}
+  BERSequenceDecoder privateKeyInfo(privateKeySource);
+  {
+    uint32_t versionNum;
+    BERDecodeUnsigned<uint32_t>(privateKeyInfo, versionNum, INTEGER);
+    BERSequenceDecoder sequenceDecoder(privateKeyInfo);
+    {
+      Oid keyTypeOid;
+      keyTypeOid.decode(sequenceDecoder);
+
+      if (keyTypeOid == oid::RSA)
+        BERDecodeNull(sequenceDecoder);
+      else if (keyTypeOid == oid::ECDSA) {
+        Oid parameterOid;
+        parameterOid.decode(sequenceDecoder);
+      }
+      else
+        return false; // Unsupported key type;
+    }
+    BERDecodeOctetString(privateKeyInfo, rawKeyBits);
+  }
+  privateKeyInfo.MessageEnd();
+
+  CFReleaser<CFDataRef> importedKey = CFDataCreateWithBytesNoCopy(0,
+                                                                  rawKeyBits.BytePtr(),
+                                                                  rawKeyBits.size(),
+                                                                  kCFAllocatorNull);
+
+  SecExternalFormat externalFormat = kSecFormatOpenSSL;
+  SecExternalItemType externalType = kSecItemTypePrivateKey;
+  SecKeyImportExportParameters keyParams;
+  memset(&keyParams, 0, sizeof(keyParams));
+  keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+  keyParams.keyAttributes = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT;
+  CFReleaser<SecAccessRef> access;
+  CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
+                                                               keyName.toUri().c_str(),
+                                                               kCFStringEncodingUTF8);
+  SecAccessCreate(keyLabel.get(), 0, &access.get());
+  keyParams.accessRef = access.get();
+  CFReleaser<CFArrayRef> outItems;
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif // __clang__
+
+  OSStatus res = SecKeychainItemImport(importedKey.get(),
+                                       0,
+                                       &externalFormat,
+                                       &externalType,
+                                       0,
+                                       &keyParams,
+                                       m_impl->m_keyChainRef,
+                                       &outItems.get());
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif // __clang__
+
+  if (res != errSecSuccess) {
+    if (res == errSecAuthFailed && !needRetry) {
+      if (unlockTpm(nullptr, 0, false))
+        return importPrivateKeyPkcs8IntoTpmInternal(keyName, buf, size, true);
+      else
+        return false;
+    }
+    else
+      return false;
+  }
+
+  // C-style cast is used as per Apple convention
+  SecKeychainItemRef privateKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
+  SecKeychainAttribute attrs[1]; // maximum number of attributes
+  SecKeychainAttributeList attrList = {0, attrs};
+  string keyUri = keyName.toUri();
+  {
+    attrs[attrList.count].tag = kSecKeyPrintName;
+    attrs[attrList.count].length = keyUri.size();
+    attrs[attrList.count].data = const_cast<char*>(keyUri.c_str());
+    attrList.count++;
+  }
+
+  res = SecKeychainItemModifyAttributesAndData(privateKey,
+                                               &attrList,
+                                               0,
+                                               nullptr);
+
+  if (res != errSecSuccess) {
+    return false;
+  }
+
+  return true;
+}
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#pragma GCC diagnostic pop
+#endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+
+bool
+SecTpmOsx::importPublicKeyPkcs1IntoTpm(const Name& keyName, const uint8_t* buf, size_t size)
+{
+  CFReleaser<CFDataRef> importedKey = CFDataCreateWithBytesNoCopy(0,
+                                                                  buf,
+                                                                  size,
+                                                                  kCFAllocatorNull);
+
+  SecExternalFormat externalFormat = kSecFormatOpenSSL;
+  SecExternalItemType externalType = kSecItemTypePublicKey;
+  CFReleaser<CFArrayRef> outItems;
+
+  OSStatus res = SecItemImport(importedKey.get(),
+                               0,
+                               &externalFormat,
+                               &externalType,
+                               0,
+                               0,
+                               m_impl->m_keyChainRef,
+                               &outItems.get());
+
+  if (res != errSecSuccess)
+    return false;
+
+  // C-style cast is used as per Apple convention
+  SecKeychainItemRef publicKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
+  SecKeychainAttribute attrs[1]; // maximum number of attributes
+  SecKeychainAttributeList attrList = { 0, attrs };
+  string keyUri = keyName.toUri();
+  {
+    attrs[attrList.count].tag = kSecKeyPrintName;
+    attrs[attrList.count].length = keyUri.size();
+    attrs[attrList.count].data = const_cast<char*>(keyUri.c_str());
+    attrList.count++;
+  }
+
+  res = SecKeychainItemModifyAttributesAndData(publicKey,
+                                               &attrList,
+                                               0,
+                                               0);
+
+  if (res != errSecSuccess)
+    return false;
+
+  return true;
+}
+
+Block
+SecTpmOsx::signInTpmInternal(const uint8_t* data, size_t dataLength,
+                             const Name& keyName, DigestAlgorithm digestAlgorithm, bool needRetry)
+{
+  CFReleaser<CFDataRef> dataRef = CFDataCreateWithBytesNoCopy(0,
+                                                              data,
+                                                              dataLength,
+                                                              kCFAllocatorNull);
+
+  CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, KeyClass::PRIVATE);
+  if (privateKey == nullptr) {
+    BOOST_THROW_EXCEPTION(Error("Private key [" + keyName.toUri() + "] does not exist "
+                                "in OSX Keychain"));
+  }
+
+  CFReleaser<CFErrorRef> error;
+  // C-style cast is used as per Apple convention
+  CFReleaser<SecTransformRef> signer = SecSignTransformCreate((SecKeyRef)privateKey.get(),
+                                                              &error.get());
+  if (error != nullptr)
+    BOOST_THROW_EXCEPTION(Error("Fail to create signer"));
+
+  // Set input
+  SecTransformSetAttribute(signer.get(),
+                           kSecTransformInputAttributeName,
+                           dataRef.get(),
+                           &error.get());
+  if (error != nullptr)
+    BOOST_THROW_EXCEPTION(Error("Fail to configure input of signer"));
+
+  // Enable use of padding
+  SecTransformSetAttribute(signer.get(),
+                           kSecPaddingKey,
+                           kSecPaddingPKCS1Key,
+                           &error.get());
+  if (error != nullptr)
+    BOOST_THROW_EXCEPTION(Error("Fail to configure digest algorithm of signer"));
+
+  // Set padding type
+  SecTransformSetAttribute(signer.get(),
+                           kSecDigestTypeAttribute,
+                           m_impl->getDigestAlgorithm(digestAlgorithm),
+                           &error.get());
+  if (error != nullptr)
+    BOOST_THROW_EXCEPTION(Error("Fail to configure digest algorithm of signer"));
+
+  // Set padding attribute
+  long digestSize = m_impl->getDigestSize(digestAlgorithm);
+  CFReleaser<CFNumberRef> cfDigestSize = CFNumberCreate(0, kCFNumberLongType, &digestSize);
+  SecTransformSetAttribute(signer.get(),
+                           kSecDigestLengthAttribute,
+                           cfDigestSize.get(),
+                           &error.get());
+  if (error != nullptr)
+    BOOST_THROW_EXCEPTION(Error("Fail to configure digest size of signer"));
+
+  // Actually sign
+  // C-style cast is used as per Apple convention
+  CFReleaser<CFDataRef> signature = (CFDataRef)SecTransformExecute(signer.get(), &error.get());
+  if (error != nullptr) {
+    if (!needRetry) {
+      if (unlockTpm(nullptr, 0, false))
+        return signInTpmInternal(data, dataLength, keyName, digestAlgorithm, true);
+      else
+        BOOST_THROW_EXCEPTION(Error("Fail to unlock the keychain"));
+    }
+    else {
+      CFShow(error.get());
+      BOOST_THROW_EXCEPTION(Error("Fail to sign data"));
+    }
+  }
+
+  if (signature == nullptr)
+    BOOST_THROW_EXCEPTION(Error("Signature is NULL!\n"));
+
+  return Block(tlv::SignatureValue,
+               make_shared<Buffer>(CFDataGetBytePtr(signature.get()),
+                                   CFDataGetLength(signature.get())));
+}
+
+ConstBufferPtr
+SecTpmOsx::decryptInTpm(const uint8_t* data, size_t dataLength, const Name& keyName, bool sym)
+{
+  BOOST_THROW_EXCEPTION(Error("SecTpmOsx::decryptInTpm is not supported"));
+
+  // KeyClass keyClass;
+  // if (sym)
+  //   keyClass = KeyClass::SYMMETRIC;
+  // else
+  //   keyClass = KeyClass::PRIVATE;
+
+  // CFDataRef dataRef = CFDataCreate(0,
+  //                                  reinterpret_cast<const unsigned char*>(data),
+  //                                  dataLength
+  //                                  );
+
+  // CFReleaser<SecKeyRef> decryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
+  // if (decryptKey == nullptr)
+  //   {
+  //     /// @todo Can this happen because of keychain is locked?
+  //     throw Error("Decruption key [" + ??? + "] does not exist in OSX Keychain");
+  //   }
+
+  // CFErrorRef error;
+  // SecTransformRef decrypt = SecDecryptTransformCreate(decryptKey, &error);
+  // if (error) throw Error("Fail to create decrypt");
+
+  // Boolean set_res = SecTransformSetAttribute(decrypt,
+  //                                            kSecTransformInputAttributeName,
+  //                                            dataRef,
+  //                                            &error);
+  // if (error) throw Error("Fail to configure decrypt");
+
+  // CFDataRef output = (CFDataRef) SecTransformExecute(decrypt, &error);
+  // if (error)
+  //   {
+  //     CFShow(error);
+  //     throw Error("Fail to decrypt data");
+  //   }
+  // if (!output) throw Error("Output is NULL!\n");
+
+  // return make_shared<Buffer>(CFDataGetBytePtr(output), CFDataGetLength(output));
+}
+
+void
+SecTpmOsx::addAppToAcl(const Name& keyName, KeyClass keyClass, const string& appPath, AclType acl)
+{
+  if (keyClass == KeyClass::PRIVATE && acl == AclType::PRIVATE) {
+    CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, keyClass);
+    if (privateKey == nullptr) {
+      BOOST_THROW_EXCEPTION(Error("Private key [" + keyName.toUri() + "] does not exist "
+                                  "in OSX Keychain"));
+    }
+
+    CFReleaser<SecAccessRef> accRef;
+    SecKeychainItemCopyAccess(privateKey.get(), &accRef.get());
+
+    CFReleaser<CFArrayRef> signACL = SecAccessCopyMatchingACLList(accRef.get(),
+                                                                  kSecACLAuthorizationSign);
+
+    // C-style cast is used as per Apple convention
+    SecACLRef aclRef = (SecACLRef)CFArrayGetValueAtIndex(signACL.get(), 0);
+
+    CFReleaser<CFArrayRef> appList;
+    CFReleaser<CFStringRef> description;
+    SecKeychainPromptSelector promptSelector;
+    SecACLCopyContents(aclRef,
+                       &appList.get(),
+                       &description.get(),
+                       &promptSelector);
+
+    CFReleaser<CFMutableArrayRef> newAppList = CFArrayCreateMutableCopy(0,
+                                                                        0,
+                                                                        appList.get());
+
+    CFReleaser<SecTrustedApplicationRef> trustedApp;
+    SecTrustedApplicationCreateFromPath(appPath.c_str(),
+                                        &trustedApp.get());
+
+    CFArrayAppendValue(newAppList.get(), trustedApp.get());
+
+    SecACLSetContents(aclRef,
+                      newAppList.get(),
+                      description.get(),
+                      promptSelector);
+
+    SecKeychainItemSetAccess(privateKey.get(), accRef.get());
+  }
+}
+
+ConstBufferPtr
+SecTpmOsx::encryptInTpm(const uint8_t* data, size_t dataLength, const Name& keyName, bool sym)
+{
+  BOOST_THROW_EXCEPTION(Error("SecTpmOsx::encryptInTpm is not supported"));
+
+  // KeyClass keyClass;
+  // if (sym)
+  //   keyClass = KeyClass::SYMMETRIC;
+  // else
+  //   keyClass = KeyClass::PUBLIC;
+
+  // CFDataRef dataRef = CFDataCreate(0,
+  //                                  reinterpret_cast<const unsigned char*>(data),
+  //                                  dataLength
+  //                                  );
+
+  // CFReleaser<SecKeyRef> encryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
+  // if (encryptKey == nullptr)
+  //   {
+  //     throw Error("Encryption key [" + ???? + "] does not exist in OSX Keychain");
+  //   }
+
+  // CFErrorRef error;
+  // SecTransformRef encrypt = SecEncryptTransformCreate(encryptKey, &error);
+  // if (error) throw Error("Fail to create encrypt");
+
+  // Boolean set_res = SecTransformSetAttribute(encrypt,
+  //                                            kSecTransformInputAttributeName,
+  //                                            dataRef,
+  //                                            &error);
+  // if (error) throw Error("Fail to configure encrypt");
+
+  // CFDataRef output = (CFDataRef) SecTransformExecute(encrypt, &error);
+  // if (error) throw Error("Fail to encrypt data");
+
+  // if (!output) throw Error("Output is NULL!\n");
+
+  // return make_shared<Buffer> (CFDataGetBytePtr(output), CFDataGetLength(output));
+}
+
+bool
+SecTpmOsx::doesKeyExistInTpm(const Name& keyName, KeyClass keyClass)
+{
+  string keyNameUri = m_impl->toInternalKeyName(keyName, keyClass);
+
+  CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
+                                                               keyNameUri.c_str(),
+                                                               kCFStringEncodingUTF8);
+
+  CFReleaser<CFMutableDictionaryRef> attrDict =
+    CFDictionaryCreateMutable(0,
+                              4,
+                              &kCFTypeDictionaryKeyCallBacks,
+                              0);
+
+  CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
+  // CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, m_impl->getKeyClass(keyClass));
+  CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
+  CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
+
+  CFReleaser<SecKeychainItemRef> itemRef;
+  // C-style cast is used as per Apple convention
+  OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&itemRef.get());
+
+  if (res == errSecSuccess)
+    return true;
+  else
+    return false;
+
+}
+
+bool
+SecTpmOsx::generateRandomBlock(uint8_t* res, size_t size)
+{
+  return SecRandomCopyBytes(kSecRandomDefault, size, res) == 0;
+}
+
+////////////////////////////////
+// OSXPrivateKeyStorage::Impl //
+////////////////////////////////
+
+CFReleaser<SecKeychainItemRef>
+SecTpmOsx::Impl::getKey(const Name& keyName, KeyClass keyClass)
+{
+  string keyNameUri = toInternalKeyName(keyName, keyClass);
+
+  CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
+                                                               keyNameUri.c_str(),
+                                                               kCFStringEncodingUTF8);
+
+  CFReleaser<CFMutableDictionaryRef> attrDict =
+    CFDictionaryCreateMutable(0,
+                              5,
+                              &kCFTypeDictionaryKeyCallBacks,
+                              0);
+
+  CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
+  CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
+  CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, getKeyClass(keyClass));
+  CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
+
+  CFReleaser<SecKeychainItemRef> keyItem;
+  // C-style cast is used as per Apple convention
+  OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&keyItem.get());
+
+  if (res != errSecSuccess)
+    return 0;
+  else
+    return keyItem;
+}
+
+string
+SecTpmOsx::Impl::toInternalKeyName(const Name& keyName, KeyClass keyClass)
+{
+  string keyUri = keyName.toUri();
+
+  if (KeyClass::SYMMETRIC == keyClass)
+    return keyUri + "/symmetric";
+  else
+    return keyUri;
+}
+
+CFTypeRef
+SecTpmOsx::Impl::getAsymKeyType(KeyType keyType)
+{
+  switch (keyType) {
+  case KeyType::RSA:
+    return kSecAttrKeyTypeRSA;
+  case KeyType::EC:
+    return kSecAttrKeyTypeECDSA;
+  default:
+    return 0;
+  }
+}
+
+CFTypeRef
+SecTpmOsx::Impl::getSymKeyType(KeyType keyType)
+{
+  switch (keyType) {
+  case KeyType::AES:
+    return kSecAttrKeyTypeAES;
+  default:
+    return 0;
+  }
+}
+
+CFTypeRef
+SecTpmOsx::Impl::getKeyClass(KeyClass keyClass)
+{
+  switch (keyClass) {
+  case KeyClass::PRIVATE:
+    return kSecAttrKeyClassPrivate;
+  case KeyClass::PUBLIC:
+    return kSecAttrKeyClassPublic;
+  case KeyClass::SYMMETRIC:
+    return kSecAttrKeyClassSymmetric;
+  default:
+    return 0;
+  }
+}
+
+CFStringRef
+SecTpmOsx::Impl::getDigestAlgorithm(DigestAlgorithm digestAlgo)
+{
+  switch (digestAlgo) {
+  case DigestAlgorithm::SHA256:
+    return kSecDigestSHA2;
+  default:
+    return 0;
+  }
+}
+
+long
+SecTpmOsx::Impl::getDigestSize(DigestAlgorithm digestAlgo)
+{
+  switch (digestAlgo) {
+  case DigestAlgorithm::SHA256:
+    return 256;
+  default:
+    return -1;
+  }
+}
+
+} // namespace v1
+} // namespace security
+} // namespace ndn