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