29 #include <Security/Security.h> 36 namespace cfstring = detail::cfstring;
37 using detail::CFReleaser;
49 return CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buf, buflen, kCFAllocatorNull);
52 static CFReleaser<CFMutableDictionaryRef>
55 return CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
56 &kCFTypeDictionaryKeyCallBacks,
57 &kCFTypeDictionaryValueCallBacks);
67 return "<no error message>";
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();
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);
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: " +
273 if (error !=
nullptr) {
274 BOOST_THROW_EXCEPTION(
Error(
"Failed to configure digest type of sign transform: " +
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: " +
290 if (signature ==
nullptr) {
294 return make_shared<Buffer>(CFDataGetBytePtr(signature.
get()), CFDataGetLength(signature.
get()));
302 if (decryptor ==
nullptr) {
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: " +
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)
356 KeyType keyType = params.getKeyType();
360 const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(params);
361 keySize = rsaParams.getKeySize();
365 const EcKeyParams& ecParams = static_cast<const EcKeyParams&>(params);
366 keySize = ecParams.getKeySize();
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) {
385 BOOST_THROW_EXCEPTION(Error(
"Failed to generate key pair: " +
getErrorMessage(res)));
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) {
420 BOOST_THROW_EXCEPTION(Error(
"Failed to delete key pair: " +
getErrorMessage(res)));
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)));
436 exportedKey.savePkcs8(pkcs8, pw, pwLen);
438 catch (
const transform::PrivateKey::Error& e) {
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)
452 privKey.loadPkcs8(buf, size, pw, pwLen);
453 privKey.savePkcs1(pkcs1);
455 catch (
const transform::PrivateKey::Error& e) {
456 BOOST_THROW_EXCEPTION(Error(
"Failed to import private key: "s + e.what()));
458 auto keyToImport =
makeCFDataNoCopy(pkcs1.buf()->data(), pkcs1.buf()->size());
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) {
471 BOOST_THROW_EXCEPTION(Error(
"Failed to import private key: " +
getErrorMessage(res)));
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) {
490 BOOST_THROW_EXCEPTION(Error(
"Failed to import private key: " +
getErrorMessage(res)));
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.
Copyright (c) 2011-2015 Regents of the University of California.
std::string toUri() const
Get URI representation of the name.
std::string toStdString(CFStringRef cfStr)
Convert a CFString to a std::string.
detail::CFReleaser< SecKeyRef > KeyRefOsx
static CFReleaser< CFMutableDictionaryRef > makeCFMutableDictionary()
SecKeychainRef keyChainRef
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)
SimplePublicKeyParams< detail::RsaKeyParamsInfo > RsaKeyParams
RsaKeyParams carries parameters for RSA key.
KeyType
The type of a cryptographic key.
static const std::string & getScheme()
bool isTerminalMode() const final
Check if the TPM is in terminal mode.
SimplePublicKeyParams< detail::EcKeyParamsInfo > EcKeyParams
EcKeyParams carries parameters for EC key.
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.
static std::string getFailureReason(CFErrorRef err)
static void exportItem(const KeyRefOsx &keyRef, transform::PrivateKey &outKey)
Export a private key from the Keychain to outKey.
shared_ptr< const Buffer > ConstBufferPtr