blob: f3c3029916adabadc79a2b6209a25fd20bd73df7 [file] [log] [blame]
/* -*- 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