| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */ |
| /** |
| * Copyright (C) 2013 Regents of the University of California. |
| * @author: Yingdi Yu <yingdi@cs.ucla.edu> |
| * See COPYING for copyright and distribution information. |
| */ |
| |
| // Only compile if ndn-cpp-config.h defines NDN_CPP_HAVE_OSX_SECURITY 1. |
| #include <ndn-cpp/ndn-cpp-config.h> |
| #if NDN_CPP_HAVE_OSX_SECURITY |
| |
| #include <fstream> |
| #include <sstream> |
| |
| #include "../../util/logging.hpp" |
| #include "osx-private-key-storage.hpp" |
| #include "../certificate/public-key.hpp" |
| |
| #include <CoreFoundation/CoreFoundation.h> |
| #include <Security/Security.h> |
| #include <CoreServices/CoreServices.h> |
| |
| using namespace std; |
| |
| INIT_LOGGER("ndn.OSXPrivateKeyStorage"); |
| |
| namespace ndn |
| { |
| class OSXPrivateKeyStorage::Impl { |
| public: |
| Impl(const std::string &keychainName) |
| : keyChainName_ ("" == keychainName ? "NDN.keychain" : keychainName) |
| { |
| } |
| |
| /** |
| * convert NDN name of a key to internal name of the key |
| * @param keyName the NDN name of the key |
| * @param keyClass the class of the key |
| * @return the internal key name |
| */ |
| std::string |
| toInternalKeyName(const Name & keyName, KeyClass keyClass); |
| |
| /** |
| * Get key |
| * @param keyName the name of the key |
| * @param keyClass the class of the key |
| * @returns pointer to the key |
| */ |
| SecKeychainItemRef |
| getKey(const Name & keyName, KeyClass keyClass); |
| |
| /** |
| * convert keyType to MAC OS symmetric key key type |
| * @param keyType |
| * @returns MAC OS key type |
| */ |
| const CFTypeRef |
| getSymKeyType(KeyType keyType); |
| |
| /** |
| * convert keyType to MAC OS asymmetirc key type |
| * @param keyType |
| * @returns MAC OS key type |
| */ |
| const CFTypeRef |
| getAsymKeyType(KeyType keyType); |
| |
| /** |
| * convert keyClass to MAC OS key class |
| * @param keyClass |
| * @returns MAC OS key class |
| */ |
| const CFTypeRef |
| getKeyClass(KeyClass keyClass); |
| |
| /** |
| * convert digestAlgo to MAC OS algorithm id |
| * @param digestAlgo |
| * @returns MAC OS algorithm id |
| */ |
| const CFStringRef |
| getDigestAlgorithm(DigestAlgorithm digestAlgo); |
| |
| /** |
| * get the digest size of the corresponding algorithm |
| * @param digestAlgo the digest algorithm |
| * @return digest size |
| */ |
| long |
| getDigestSize(DigestAlgorithm digestAlgo); |
| |
| /////////////////////////////////////////////// |
| // everything here is public, including data // |
| /////////////////////////////////////////////// |
| public: |
| const std::string keyChainName_; |
| SecKeychainRef keyChainRef_; |
| SecKeychainRef originalDefaultKeyChain_; |
| }; |
| |
| |
| |
| OSXPrivateKeyStorage::OSXPrivateKeyStorage(const string & keychainName) |
| : impl_(new Impl(keychainName)) |
| { |
| OSStatus res = SecKeychainCreate(impl_->keyChainName_.c_str(), //Keychain path |
| 0, //Keychain password length |
| NULL, //Keychain password |
| true, //User prompt |
| NULL, //Initial access of Keychain |
| &impl_->keyChainRef_); //Keychain reference |
| |
| if (res == errSecDuplicateKeychain) |
| res = SecKeychainOpen(impl_->keyChainName_.c_str(), |
| &impl_->keyChainRef_); |
| |
| if (res != errSecSuccess){ |
| _LOG_DEBUG("Fail to initialize keychain ref: " << res); |
| throw Error("Fail to initialize keychain ref"); |
| } |
| |
| res = SecKeychainCopyDefault(&impl_->originalDefaultKeyChain_); |
| |
| res = SecKeychainSetDefault(impl_->keyChainRef_); |
| if (res != errSecSuccess){ |
| _LOG_DEBUG("Fail to set default keychain: " << res); |
| throw Error("Fail to set default keychain"); |
| } |
| } |
| |
| OSXPrivateKeyStorage::~OSXPrivateKeyStorage(){ |
| //TODO: implement |
| } |
| |
| void |
| OSXPrivateKeyStorage::generateKeyPair(const Name & keyName, KeyType keyType, int keySize) |
| { |
| |
| if(doesKeyExist(keyName, KEY_CLASS_PUBLIC)){ |
| _LOG_DEBUG("keyName has existed"); |
| throw Error("keyName has existed"); |
| } |
| |
| string keyNameUri = impl_->toInternalKeyName(keyName, KEY_CLASS_PUBLIC); |
| |
| SecKeyRef publicKey, privateKey; |
| |
| CFStringRef keyLabel = CFStringCreateWithCString(NULL, |
| keyNameUri.c_str(), |
| kCFStringEncodingUTF8); |
| |
| CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL, |
| 3, |
| &kCFTypeDictionaryKeyCallBacks, |
| NULL); |
| |
| CFDictionaryAddValue(attrDict, kSecAttrKeyType, impl_->getAsymKeyType(keyType)); |
| CFDictionaryAddValue(attrDict, kSecAttrKeySizeInBits, CFNumberCreate(NULL, kCFNumberIntType, &keySize)); |
| CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel); |
| |
| OSStatus res = SecKeyGeneratePair((CFDictionaryRef)attrDict, &publicKey, &privateKey); |
| |
| CFRelease(publicKey); |
| CFRelease(privateKey); |
| |
| if (res != errSecSuccess){ |
| _LOG_DEBUG("Fail to create a key pair: " << res); |
| throw Error("Fail to create a key pair"); |
| } |
| } |
| |
| void |
| OSXPrivateKeyStorage::generateKey(const Name & keyName, KeyType keyType, int keySize) |
| { |
| |
| if(doesKeyExist(keyName, KEY_CLASS_SYMMETRIC)) |
| throw Error("keyName has existed!"); |
| |
| string keyNameUri = impl_->toInternalKeyName(keyName, KEY_CLASS_SYMMETRIC); |
| |
| CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(kCFAllocatorDefault, |
| 0, |
| &kCFTypeDictionaryKeyCallBacks, |
| &kCFTypeDictionaryValueCallBacks); |
| |
| CFStringRef keyLabel = CFStringCreateWithCString(NULL, |
| keyNameUri.c_str(), |
| kCFStringEncodingUTF8); |
| |
| CFDictionaryAddValue(attrDict, kSecAttrKeyType, impl_->getSymKeyType(keyType)); |
| CFDictionaryAddValue(attrDict, kSecAttrKeySizeInBits, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &keySize)); |
| CFDictionaryAddValue(attrDict, kSecAttrIsPermanent, kCFBooleanTrue); |
| CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel); |
| |
| CFErrorRef error = NULL; |
| |
| SecKeyRef symmetricKey = SecKeyGenerateSymmetric(attrDict, &error); |
| |
| if (error) |
| throw Error("Fail to create a symmetric key"); |
| } |
| |
| ptr_lib::shared_ptr<PublicKey> |
| OSXPrivateKeyStorage::getPublicKey(const Name & keyName) |
| { |
| _LOG_TRACE("OSXPrivateKeyStorage::getPublickey"); |
| |
| SecKeychainItemRef publicKey = impl_->getKey(keyName, KEY_CLASS_PUBLIC); |
| |
| CFDataRef exportedKey; |
| |
| OSStatus res = SecItemExport(publicKey, |
| kSecFormatOpenSSL, |
| 0, |
| NULL, |
| &exportedKey); |
| |
| return ptr_lib::make_shared<PublicKey>(CFDataGetBytePtr(exportedKey), CFDataGetLength(exportedKey)); |
| } |
| |
| Block |
| OSXPrivateKeyStorage::sign(const uint8_t *data, size_t dataLength, |
| const Name& keyName, DigestAlgorithm digestAlgorithm/* = DIGEST_ALGORITHM_SHA256*/) |
| { |
| _LOG_TRACE("OSXPrivateKeyStorage::Sign"); |
| |
| CFDataRef dataRef = CFDataCreateWithBytesNoCopy(NULL, |
| data, |
| dataLength, |
| kCFAllocatorNull |
| ); |
| |
| SecKeyRef privateKey = (SecKeyRef)impl_->getKey(keyName, KEY_CLASS_PRIVATE); |
| |
| CFErrorRef error; |
| SecTransformRef signer = SecSignTransformCreate((SecKeyRef)privateKey, &error); |
| if (error) throw Error("Fail to create signer"); |
| |
| // Set input |
| Boolean set_res = SecTransformSetAttribute(signer, |
| kSecTransformInputAttributeName, |
| dataRef, |
| &error); |
| if (error) throw Error("Fail to configure input of signer"); |
| |
| // Enable use of padding |
| SecTransformSetAttribute( |
| signer, |
| kSecPaddingKey, |
| kSecPaddingPKCS1Key, |
| &error); |
| if (error) throw Error("Fail to configure digest algorithm of signer"); |
| |
| // Set padding type |
| set_res = SecTransformSetAttribute(signer, |
| kSecDigestTypeAttribute, |
| impl_->getDigestAlgorithm(digestAlgorithm), |
| &error); |
| if (error) throw Error("Fail to configure digest algorithm of signer"); |
| |
| // Set padding attribute |
| long digestSize = impl_->getDigestSize(digestAlgorithm); |
| set_res = SecTransformSetAttribute(signer, |
| kSecDigestLengthAttribute, |
| CFNumberCreate(NULL, kCFNumberLongType, &digestSize), |
| &error); |
| if (error) throw Error("Fail to configure digest size of signer"); |
| |
| // Actually sign |
| CFDataRef signature = (CFDataRef) SecTransformExecute(signer, &error); |
| if (error) { |
| CFShow(error); |
| throw Error("Fail to sign data"); |
| } |
| |
| if (!signature) throw Error("Signature is NULL!\n"); |
| |
| return Block(Tlv::SignatureValue, ptr_lib::make_shared<Buffer>(CFDataGetBytePtr(signature), CFDataGetLength(signature))); |
| } |
| |
| void |
| OSXPrivateKeyStorage::sign(Data &data, |
| const Name& keyName, DigestAlgorithm digestAlgorithm/* = DIGEST_ALGORITHM_SHA256 */) |
| { |
| } |
| |
| ConstBufferPtr |
| OSXPrivateKeyStorage::decrypt(const Name & keyName, const uint8_t* data, size_t dataLength, bool sym) |
| { |
| _LOG_TRACE("OSXPrivateKeyStorage::Decrypt"); |
| |
| KeyClass keyClass; |
| if(sym) |
| keyClass = KEY_CLASS_SYMMETRIC; |
| else |
| keyClass = KEY_CLASS_PRIVATE; |
| |
| CFDataRef dataRef = CFDataCreate(NULL, |
| reinterpret_cast<const unsigned char*>(data), |
| dataLength |
| ); |
| |
| // _LOG_DEBUG("CreateData"); |
| |
| SecKeyRef decryptKey = (SecKeyRef)impl_->getKey(keyName, keyClass); |
| |
| // _LOG_DEBUG("GetKey"); |
| |
| 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 ptr_lib::make_shared<Buffer>(CFDataGetBytePtr(output), CFDataGetLength(output)); |
| } |
| |
| bool |
| OSXPrivateKeyStorage::setACL(const Name & keyName, KeyClass keyClass, int acl, const string & appPath) |
| { |
| SecKeychainItemRef privateKey = impl_->getKey(keyName, keyClass); |
| |
| SecAccessRef accRef; |
| OSStatus acc_res = SecKeychainItemCopyAccess(privateKey, &accRef); |
| |
| CFArrayRef signACL = SecAccessCopyMatchingACLList(accRef, |
| kSecACLAuthorizationSign); |
| |
| SecACLRef aclRef = (SecACLRef) CFArrayGetValueAtIndex(signACL, 0); |
| |
| CFArrayRef appList; |
| CFStringRef description; |
| SecKeychainPromptSelector promptSelector; |
| OSStatus acl_res = SecACLCopyContents(aclRef, |
| &appList, |
| &description, |
| &promptSelector); |
| |
| CFMutableArrayRef newAppList = CFArrayCreateMutableCopy(NULL, |
| 0, |
| appList); |
| |
| SecTrustedApplicationRef trustedApp; |
| acl_res = SecTrustedApplicationCreateFromPath(appPath.c_str(), |
| &trustedApp); |
| |
| CFArrayAppendValue(newAppList, trustedApp); |
| |
| |
| CFArrayRef authList = SecACLCopyAuthorizations(aclRef); |
| |
| acl_res = SecACLRemove(aclRef); |
| |
| SecACLRef newACL; |
| acl_res = SecACLCreateWithSimpleContents(accRef, |
| newAppList, |
| description, |
| promptSelector, |
| &newACL); |
| |
| acl_res = SecACLUpdateAuthorizations(newACL, authList); |
| |
| acc_res = SecKeychainItemSetAccess(privateKey, accRef); |
| |
| return true; |
| } |
| |
| // bool |
| // OSXPrivateKeyStorage::verifyData(const Name & keyName, const Blob & pData, const Blob & pSig, DigestAlgorithm digestAlgo) |
| // { |
| // _LOG_TRACE("OSXPrivateKeyStorage::Verify"); |
| |
| // CFDataRef dataRef = CFDataCreate(NULL, |
| // reinterpret_cast<const unsigned char*>(pData.buf()), |
| // pData.size()); |
| |
| // CFDataRef sigRef = CFDataCreate(NULL, |
| // reinterpret_cast<const unsigned char*>(pSig.buf()), |
| // pSig.size()); |
| |
| // SecKeyRef publicKey = (SecKeyRef)impl_->getKey(keyName, KEY_CLASS_PUBLIC); |
| |
| // CFErrorRef error; |
| // SecTransformRef verifier = SecVerifyTransformCreate(publicKey, sigRef, &error); |
| // if (error) throw Error("Fail to create verifier"); |
| |
| // Boolean set_res = SecTransformSetAttribute(verifier, |
| // kSecTransformInputAttributeName, |
| // dataRef, |
| // &error); |
| // if (error) throw Error("Fail to configure input of verifier"); |
| |
| // set_res = SecTransformSetAttribute(verifier, |
| // kSecDigestTypeAttribute, |
| // impl_->getDigestAlgorithm(digestAlgo), |
| // &error); |
| // if (error) throw Error("Fail to configure digest algorithm of verifier"); |
| |
| // long digestSize = impl_->getDigestSize(digestAlgo); |
| // set_res = SecTransformSetAttribute(verifier, |
| // kSecDigestLengthAttribute, |
| // CFNumberCreate(NULL, kCFNumberLongType, &digestSize), |
| // &error); |
| // if (error) throw Error("Fail to configure digest size of verifier"); |
| |
| // CFBooleanRef result = (CFBooleanRef) SecTransformExecute(verifier, &error); |
| // if (error) throw Error("Fail to verify data"); |
| |
| // if (result == kCFBooleanTrue) |
| // return true; |
| // else |
| // return false; |
| // } |
| |
| ConstBufferPtr |
| OSXPrivateKeyStorage::encrypt(const Name & keyName, const uint8_t* data, size_t dataLength, bool sym) |
| { |
| _LOG_TRACE("OSXPrivateKeyStorage::Encrypt"); |
| |
| KeyClass keyClass; |
| if(sym) |
| keyClass = KEY_CLASS_SYMMETRIC; |
| else |
| keyClass = KEY_CLASS_PUBLIC; |
| |
| CFDataRef dataRef = CFDataCreate(NULL, |
| reinterpret_cast<const unsigned char*>(data), |
| dataLength |
| ); |
| |
| SecKeyRef encryptKey = (SecKeyRef)impl_->getKey(keyName, keyClass); |
| |
| 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 ptr_lib::make_shared<Buffer> (CFDataGetBytePtr(output), CFDataGetLength(output)); |
| } |
| |
| bool |
| OSXPrivateKeyStorage::doesKeyExist(const Name & keyName, KeyClass keyClass) |
| { |
| _LOG_TRACE("OSXPrivateKeyStorage::doesKeyExist"); |
| |
| string keyNameUri = impl_->toInternalKeyName(keyName, keyClass); |
| |
| CFStringRef keyLabel = CFStringCreateWithCString(NULL, |
| keyNameUri.c_str(), |
| kCFStringEncodingUTF8); |
| |
| CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL, |
| 3, |
| &kCFTypeDictionaryKeyCallBacks, |
| NULL); |
| |
| CFDictionaryAddValue(attrDict, kSecAttrKeyClass, impl_->getKeyClass(keyClass)); |
| CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel); |
| CFDictionaryAddValue(attrDict, kSecReturnRef, kCFBooleanTrue); |
| |
| SecKeychainItemRef itemRef; |
| OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict, (CFTypeRef*)&itemRef); |
| |
| if(res == errSecItemNotFound) |
| return true; |
| else |
| return false; |
| |
| } |
| |
| |
| //////////////////////////////// |
| // OSXPrivateKeyStorage::Impl // |
| //////////////////////////////// |
| |
| SecKeychainItemRef |
| OSXPrivateKeyStorage::Impl::getKey(const Name & keyName, KeyClass keyClass) |
| { |
| string keyNameUri = toInternalKeyName(keyName, keyClass); |
| |
| CFStringRef keyLabel = CFStringCreateWithCString(NULL, |
| keyNameUri.c_str(), |
| kCFStringEncodingUTF8); |
| |
| CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL, |
| 5, |
| &kCFTypeDictionaryKeyCallBacks, |
| NULL); |
| |
| CFDictionaryAddValue(attrDict, kSecClass, kSecClassKey); |
| CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel); |
| CFDictionaryAddValue(attrDict, kSecAttrKeyClass, getKeyClass(keyClass)); |
| CFDictionaryAddValue(attrDict, kSecReturnRef, kCFBooleanTrue); |
| |
| SecKeychainItemRef keyItem; |
| |
| OSStatus res = SecItemCopyMatching((CFDictionaryRef) attrDict, (CFTypeRef*)&keyItem); |
| |
| if(res != errSecSuccess){ |
| _LOG_DEBUG("Fail to find the key!"); |
| return NULL; |
| } |
| else |
| return keyItem; |
| } |
| |
| string OSXPrivateKeyStorage::Impl::toInternalKeyName(const Name & keyName, KeyClass keyClass) |
| { |
| string keyUri = keyName.toUri(); |
| |
| if(KEY_CLASS_SYMMETRIC == keyClass) |
| return keyUri + "/symmetric"; |
| else |
| return keyUri; |
| } |
| |
| const CFTypeRef OSXPrivateKeyStorage::Impl::getAsymKeyType(KeyType keyType) |
| { |
| switch(keyType){ |
| case KEY_TYPE_RSA: |
| return kSecAttrKeyTypeRSA; |
| default: |
| _LOG_DEBUG("Unrecognized key type!") |
| return NULL; |
| } |
| } |
| |
| const CFTypeRef OSXPrivateKeyStorage::Impl::getSymKeyType(KeyType keyType) |
| { |
| switch(keyType){ |
| case KEY_TYPE_AES: |
| return kSecAttrKeyTypeAES; |
| default: |
| _LOG_DEBUG("Unrecognized key type!") |
| return NULL; |
| } |
| } |
| |
| const CFTypeRef OSXPrivateKeyStorage::Impl::getKeyClass(KeyClass keyClass) |
| { |
| switch(keyClass){ |
| case KEY_CLASS_PRIVATE: |
| return kSecAttrKeyClassPrivate; |
| case KEY_CLASS_PUBLIC: |
| return kSecAttrKeyClassPublic; |
| case KEY_CLASS_SYMMETRIC: |
| return kSecAttrKeyClassSymmetric; |
| default: |
| _LOG_DEBUG("Unrecognized key class!"); |
| return NULL; |
| } |
| } |
| |
| const CFStringRef OSXPrivateKeyStorage::Impl::getDigestAlgorithm(DigestAlgorithm digestAlgo) |
| { |
| switch(digestAlgo){ |
| // case DIGEST_MD2: |
| // return kSecDigestMD2; |
| // case DIGEST_MD5: |
| // return kSecDigestMD5; |
| // case DIGEST_SHA1: |
| // return kSecDigestSHA1; |
| case DIGEST_ALGORITHM_SHA256: |
| return kSecDigestSHA2; |
| default: |
| _LOG_DEBUG("Unrecognized digest algorithm!"); |
| return NULL; |
| } |
| } |
| |
| long OSXPrivateKeyStorage::Impl::getDigestSize(DigestAlgorithm digestAlgo) |
| { |
| switch(digestAlgo){ |
| case DIGEST_ALGORITHM_SHA256: |
| return 256; |
| // case DIGEST_SHA1: |
| // case DIGEST_MD2: |
| // case DIGEST_MD5: |
| // return 0; |
| default: |
| _LOG_DEBUG("Unrecognized digest algorithm! Unknown digest size"); |
| return -1; |
| } |
| } |
| |
| } |
| |
| #endif // NDN_CPP_HAVE_OSX_SECURITY |