29 #include <Security/Security.h> 36 namespace cfstring = detail::cfstring;
37 using detail::CFReleaser;
39 class BackEndOsx::Impl
42 SecKeychainRef keyChainRef;
46 static CFReleaser<CFDataRef>
49 return CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buf, buflen, kCFAllocatorNull);
52 static CFReleaser<CFMutableDictionaryRef>
55 return CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
56 &kCFTypeDictionaryKeyCallBacks,
57 &kCFTypeDictionaryValueCallBacks);
63 CFReleaser<CFStringRef> msg = SecCopyErrorMessageString(status,
nullptr);
67 return "<no error message>";
73 CFReleaser<CFStringRef> reason = CFErrorCopyFailureReason(err);
74 if (reason !=
nullptr)
77 return "<unknown reason>";
85 return kSecAttrKeyTypeRSA;
87 return kSecAttrKeyTypeECDSA;
89 BOOST_THROW_EXCEPTION(
Tpm::Error(
"Unsupported key type"));
101 return kSecDigestSHA2;
110 switch (digestAlgo) {
133 CFDictionaryAddValue(query.get(), kSecClass, kSecClassKey);
134 CFDictionaryAddValue(query.get(), kSecAttrKeyClass, kSecAttrKeyClassPrivate);
135 CFDictionaryAddValue(query.get(), kSecAttrLabel, keyLabel.get());
136 CFDictionaryAddValue(query.get(), kSecReturnRef, kCFBooleanTrue);
140 OSStatus res = SecItemCopyMatching(query.get(), (CFTypeRef*)&keyRef.get());
143 if (res == errSecSuccess) {
146 else if (res == errSecItemNotFound) {
161 const char pw[] =
"correct horse battery staple";
164 SecItemImportExportKeyParameters keyParams;
165 std::memset(&keyParams, 0,
sizeof(keyParams));
166 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
167 keyParams.passphrase = passphrase.get();
169 CFReleaser<CFDataRef> exportedKey;
170 OSStatus res = SecItemExport(keyRef.
get(),
171 kSecFormatWrappedPKCS8,
176 if (res != errSecSuccess) {
180 outKey.
loadPkcs8(CFDataGetBytePtr(exportedKey.get()), CFDataGetLength(exportedKey.get()),
181 pw, std::strlen(pw));
185 : m_impl(make_unique<Impl>())
187 SecKeychainSetUserInteractionAllowed(!m_impl->isTerminalMode);
189 OSStatus res = SecKeychainCopyDefault(&m_impl->keyChainRef);
190 if (res == errSecNoDefaultKeychain) {
191 BOOST_THROW_EXCEPTION(
Error(
"No default keychain, create one first"));
200 static std::string scheme =
"tpm-osxkeychain";
207 return m_impl->isTerminalMode;
213 m_impl->isTerminalMode = isTerminal;
214 SecKeychainSetUserInteractionAllowed(!isTerminal);
220 SecKeychainStatus keychainStatus;
221 OSStatus res = SecKeychainGetStatus(m_impl->keyChainRef, &keychainStatus);
222 if (res != errSecSuccess)
225 return (kSecUnlockStateStatus & keychainStatus) == 0;
235 if (m_impl->isTerminalMode) {
237 SecKeychainUnlock(m_impl->keyChainRef, pwLen, pw,
true);
241 SecKeychainUnlock(m_impl->keyChainRef, 0,
nullptr,
false);
250 CFReleaser<CFErrorRef> error;
251 CFReleaser<SecTransformRef> signer = SecSignTransformCreate(key.
get(), &error.get());
252 if (signer ==
nullptr) {
258 SecTransformSetAttribute(signer.get(), kSecTransformInputAttributeName, data.get(), &error.get());
259 if (error !=
nullptr) {
260 BOOST_THROW_EXCEPTION(
Error(
"Failed to configure input of sign transform: " +
265 SecTransformSetAttribute(signer.get(), kSecPaddingKey, kSecPaddingPKCS1Key, &error.get());
266 if (error !=
nullptr) {
267 BOOST_THROW_EXCEPTION(
Error(
"Failed to configure padding of sign transform: " +
272 SecTransformSetAttribute(signer.get(), kSecDigestTypeAttribute,
getDigestAlgorithm(digestAlgo), &error.get());
273 if (error !=
nullptr) {
274 BOOST_THROW_EXCEPTION(
Error(
"Failed to configure digest type of sign transform: " +
280 CFReleaser<CFNumberRef> cfDigestSize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &digestSize);
281 SecTransformSetAttribute(signer.get(), kSecDigestLengthAttribute, cfDigestSize.get(), &error.get());
282 if (error !=
nullptr) {
283 BOOST_THROW_EXCEPTION(
Error(
"Failed to configure digest length of sign transform: " +
289 CFReleaser<CFDataRef> signature = (CFDataRef)SecTransformExecute(signer.get(), &error.get());
290 if (signature ==
nullptr) {
294 return make_shared<Buffer>(CFDataGetBytePtr(signature.get()), CFDataGetLength(signature.get()));
300 CFReleaser<CFErrorRef> error;
301 CFReleaser<SecTransformRef> decryptor = SecDecryptTransformCreate(key.
get(), &error.get());
302 if (decryptor ==
nullptr) {
303 BOOST_THROW_EXCEPTION(
Error(
"Failed to create decrypt transform: " +
getFailureReason(error.get())));
307 SecTransformSetAttribute(decryptor.get(), kSecTransformInputAttributeName, data.get(), &error.get());
308 if (error !=
nullptr) {
309 BOOST_THROW_EXCEPTION(
Error(
"Failed to configure input of decrypt transform: " +
313 SecTransformSetAttribute(decryptor.get(), kSecPaddingKey, kSecPaddingOAEPKey, &error.get());
314 if (error !=
nullptr) {
315 BOOST_THROW_EXCEPTION(
Error(
"Failed to configure padding of decrypt transform: " +
320 CFReleaser<CFDataRef> plainText = (CFDataRef)SecTransformExecute(decryptor.get(), &error.get());
321 if (plainText ==
nullptr) {
325 return make_shared<Buffer>(CFDataGetBytePtr(plainText.get()), CFDataGetLength(plainText.get()));
337 BackEndOsx::doHasKey(
const Name& keyName)
const 342 unique_ptr<KeyHandle>
343 BackEndOsx::doGetKeyHandle(
const Name& keyName)
const 346 if (keyRef ==
nullptr) {
350 return make_unique<KeyHandleOsx>(keyRef.
get());
353 unique_ptr<KeyHandle>
354 BackEndOsx::doCreateKey(
const Name& identityName,
const KeyParams& params)
370 BOOST_THROW_EXCEPTION(
Tpm::Error(
"Failed to generate key pair: Unsupported key type"));
373 CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize);
376 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType,
getAsymKeyType(keyType));
377 CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
380 OSStatus res = SecKeyGeneratePair(attrDict.get(), &publicKey.get(), &privateKey.get());
384 if (res != errSecSuccess) {
388 unique_ptr<KeyHandle> keyHandle = make_unique<KeyHandleOsx>(privateKey.get());
391 SecKeychainAttribute attrs[1];
392 SecKeychainAttributeList attrList = {0, attrs};
393 std::string keyUri = keyHandle->getKeyName().toUri();
395 attrs[attrList.count].tag = kSecKeyPrintName;
396 attrs[attrList.count].length = keyUri.size();
397 attrs[attrList.count].data =
const_cast<char*
>(keyUri.data());
401 SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)privateKey.get(), &attrList, 0,
nullptr);
402 SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)publicKey.get(), &attrList, 0,
nullptr);
408 BackEndOsx::doDeleteKey(
const Name& keyName)
413 CFDictionaryAddValue(query.get(), kSecClass, kSecClassKey);
414 CFDictionaryAddValue(query.get(), kSecAttrLabel, keyLabel.get());
415 CFDictionaryAddValue(query.get(), kSecMatchLimit, kSecMatchLimitAll);
417 OSStatus res = SecItemDelete(query.get());
419 if (res != errSecSuccess && res != errSecItemNotFound) {
425 BackEndOsx::doExportKey(
const Name& keyName,
const char* pw,
size_t pwLen)
428 if (keychainItem ==
nullptr) {
429 BOOST_THROW_EXCEPTION(
Error(
"Failed to export private key: " +
getErrorMessage(errSecItemNotFound)));
439 BOOST_THROW_EXCEPTION(
Error(
"Failed to export private key: "s + e.what()));
445 BackEndOsx::doImportKey(
const Name& keyName,
const uint8_t* buf,
size_t size,
446 const char* pw,
size_t pwLen)
456 BOOST_THROW_EXCEPTION(
Error(
"Failed to import private key: "s + e.what()));
460 SecExternalFormat externalFormat = kSecFormatOpenSSL;
461 SecExternalItemType externalType = kSecItemTypePrivateKey;
463 auto keyUri = keyName.
toUri();
465 CFReleaser<SecAccessRef> access;
466 OSStatus res = SecAccessCreate(keyLabel.get(),
470 if (res != errSecSuccess) {
474 SecItemImportExportKeyParameters keyParams;
475 std::memset(&keyParams, 0,
sizeof(keyParams));
476 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
477 keyParams.accessRef = access.get();
479 CFReleaser<CFArrayRef> outItems;
480 res = SecItemImport(keyToImport.get(),
489 if (res != errSecSuccess) {
494 SecKeychainItemRef keychainItem = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
495 SecKeychainAttribute attrs[1];
496 SecKeychainAttributeList attrList = {0, attrs};
498 attrs[attrList.count].tag = kSecKeyPrintName;
499 attrs[attrList.count].length = keyUri.size();
500 attrs[attrList.count].data =
const_cast<char*
>(keyUri.data());
503 SecKeychainItemModifyAttributesAndData(keychainItem, &attrList, 0,
nullptr);
static CFReleaser< CFDataRef > makeCFDataNoCopy(const uint8_t *buf, size_t buflen)
CFReleaser< CFStringRef > fromStdString(const std::string &str)
Create a CFString by copying characters from a std::string.
This file contains utilities to deal with Apple Core Foundation's CFString and related types...
static ConstBufferPtr decrypt(const KeyRefOsx &key, const uint8_t *cipherText, size_t cipherSize)
bool isTpmLocked() const final
Check if the TPM is locked.
std::string toStdString(CFStringRef cfStr)
Convert a CFString to a std::string.
static CFReleaser< CFMutableDictionaryRef > makeCFMutableDictionary()
KeyType getKeyType() const
uint32_t getKeySize() const
RSA key, supports sign/verify and encrypt/decrypt operations.
static CFTypeRef getDigestAlgorithm(DigestAlgorithm digestAlgo)
static KeyRefOsx getKeyRef(const Name &keyName)
Get reference to private key with name keyName.
static std::string getErrorMessage(OSStatus status)
KeyType
The type of a cryptographic key.
static const std::string & getScheme()
std::string toUri() const
Get URI representation of the name.
bool isTerminalMode() const final
Check if the TPM is in terminal mode.
void setTerminalMode(bool isTerminal) const final
Set the terminal mode of the TPM.
void retain(const T &typeRef)
static CFTypeRef getAsymKeyType(KeyType keyType)
static int getDigestSize(DigestAlgorithm digestAlgo)
static ConstBufferPtr derivePublicKey(const KeyRefOsx &key)
BackEndOsx(const std::string &location="")
Create TPM backed based on macOS Keychain Services.
bool unlockTpm(const char *pw, size_t pwLen) const final
Unlock the TPM.
Elliptic Curve key (e.g. for ECDSA), supports sign/verify operations.
Use the SHA256 hash of the public key as the key id.
Represents an absolute name.
static void setKeyName(KeyHandle &keyHandle, const Name &identity, const KeyParams ¶ms)
Set the key name in keyHandle according to identity and params.
Helper class to wrap CoreFoundation object pointers.
static ConstBufferPtr sign(const KeyRefOsx &key, DigestAlgorithm digestAlgorithm, const uint8_t *buf, size_t size)
Sign buf with key using digestAlgorithm.
CFReleaser< CFStringRef > fromBuffer(const uint8_t *buf, size_t buflen)
Create a CFString by copying bytes from a raw buffer.
shared_ptr< Buffer > buf()
Flush written data to the stream and return shared pointer to the underlying buffer.
Base class of key parameters.
static std::string getFailureReason(CFErrorRef err)
implements an output stream that constructs ndn::Buffer
static void exportItem(const KeyRefOsx &keyRef, transform::PrivateKey &outKey)
Export a private key from the Keychain to outKey.
SimplePublicKeyParams is a template for public keys with only one parameter: size.
shared_ptr< const Buffer > ConstBufferPtr