25 #include "../transform/private-key.hpp" 27 #include <CoreServices/CoreServices.h> 28 #include <Security/SecDigestTransform.h> 29 #include <Security/SecRandom.h> 30 #include <Security/Security.h> 36 using util::CFReleaser;
52 CFReleaser<SecKeychainItemRef>
55 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(
nullptr, keyName.
toUri().c_str(),
56 kCFStringEncodingUTF8);
58 CFReleaser<CFMutableDictionaryRef> attrDict =
59 CFDictionaryCreateMutable(
nullptr, 5, &kCFTypeDictionaryKeyCallBacks,
nullptr);
61 CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
62 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
63 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, kSecAttrKeyClassPrivate);
64 CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
66 CFReleaser<SecKeychainItemRef> keyItem;
68 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&keyItem.get());
71 if (res != errSecSuccess) {
72 if (res == errSecAuthFailed) {
73 BOOST_THROW_EXCEPTION(
Error(
"Fail to unlock the keychain"));
75 BOOST_THROW_EXCEPTION(std::domain_error(
"Key does not exist"));
92 return kSecAttrKeyTypeRSA;
94 return kSecAttrKeyTypeECDSA;
96 BOOST_THROW_EXCEPTION(
Tpm::Error(
"Unsupported key type"));
103 switch (digestAlgo) {
108 return kSecDigestSHA2;
117 switch (digestAlgo) {
134 SecKeychainSetUserInteractionAllowed(!m_impl->isTerminalMode);
136 OSStatus res = SecKeychainCopyDefault(&m_impl->keyChainRef);
138 if (res == errSecNoDefaultKeychain) {
139 BOOST_THROW_EXCEPTION(
Error(
"No default keychain, create one first"));
148 static std::string scheme =
"tpm-osxkeychain";
155 return m_impl->isTerminalMode;
161 m_impl->isTerminalMode = isTerminal;
162 SecKeychainSetUserInteractionAllowed(!isTerminal);
168 SecKeychainStatus keychainStatus;
170 OSStatus res = SecKeychainGetStatus(m_impl->keyChainRef, &keychainStatus);
171 if (res != errSecSuccess)
174 return ((kSecUnlockStateStatus & keychainStatus) == 0);
184 if (m_impl->isTerminalMode) {
186 SecKeychainUnlock(m_impl->keyChainRef, pwLen, pw,
true);
190 SecKeychainUnlock(m_impl->keyChainRef, 0,
nullptr,
false);
199 CFReleaser<CFErrorRef> error;
200 CFReleaser<SecTransformRef> signer = SecSignTransformCreate(key.
get(), &error.get());
201 if (error !=
nullptr) {
202 BOOST_THROW_EXCEPTION(
Error(
"Fail to create signer"));
206 CFReleaser<CFDataRef> dataRef = CFDataCreateWithBytesNoCopy(
nullptr, buf, size, kCFAllocatorNull);
207 SecTransformSetAttribute(signer.get(), kSecTransformInputAttributeName, dataRef.get(), &error.get());
208 if (error !=
nullptr) {
209 BOOST_THROW_EXCEPTION(
Error(
"Fail to configure input of signer"));
213 SecTransformSetAttribute(signer.get(), kSecPaddingKey, kSecPaddingPKCS1Key, &error.get());
214 if (error !=
nullptr) {
215 BOOST_THROW_EXCEPTION(
Error(
"Fail to configure padding of signer"));
219 SecTransformSetAttribute(signer.get(), kSecDigestTypeAttribute,
getDigestAlgorithm(digestAlgo), &error.get());
220 if (error !=
nullptr) {
221 BOOST_THROW_EXCEPTION(
Error(
"Fail to configure digest type of signer"));
226 CFReleaser<CFNumberRef> cfDigestSize = CFNumberCreate(
nullptr, kCFNumberLongType, &digestSize);
227 SecTransformSetAttribute(signer.get(), kSecDigestLengthAttribute, cfDigestSize.get(), &error.get());
228 if (error !=
nullptr) {
229 BOOST_THROW_EXCEPTION(
Error(
"Fail to configure digest length of signer"));
234 CFReleaser<CFDataRef> signature = (CFDataRef)SecTransformExecute(signer.get(), &error.get());
235 if (error !=
nullptr) {
237 BOOST_THROW_EXCEPTION(
Error(
"Fail to sign data"));
240 if (signature ==
nullptr) {
241 BOOST_THROW_EXCEPTION(
Error(
"Signature is null"));
244 return make_shared<Buffer>(CFDataGetBytePtr(signature.get()), CFDataGetLength(signature.get()));
250 CFReleaser<CFErrorRef> error;
251 CFReleaser<SecTransformRef> decryptor = SecDecryptTransformCreate(key.
get(), &error.get());
252 if (error !=
nullptr) {
253 BOOST_THROW_EXCEPTION(
Error(
"Fail to create decryptor"));
256 CFReleaser<CFDataRef> dataRef = CFDataCreateWithBytesNoCopy(
nullptr, cipherText, cipherSize, kCFAllocatorNull);
257 SecTransformSetAttribute(decryptor.get(), kSecTransformInputAttributeName, dataRef.get(), &error.get());
258 if (error !=
nullptr) {
259 BOOST_THROW_EXCEPTION(
Error(
"Fail to configure decryptor input"));
262 SecTransformSetAttribute(decryptor.get(), kSecPaddingKey, kSecPaddingOAEPKey, &error.get());
263 if (error !=
nullptr) {
264 BOOST_THROW_EXCEPTION(
Error(
"Fail to configure decryptor padding"));
267 CFReleaser<CFDataRef> output = (CFDataRef)SecTransformExecute(decryptor.get(), &error.get());
268 if (error !=
nullptr) {
270 BOOST_THROW_EXCEPTION(
Error(
"Fail to decrypt data"));
273 if (output ==
nullptr) {
274 BOOST_THROW_EXCEPTION(
Error(
"Output is null"));
277 return make_shared<Buffer>(CFDataGetBytePtr(output.get()), CFDataGetLength(output.get()));
283 CFReleaser<CFDataRef> exportedKey;
284 OSStatus res = SecItemExport(key.
get(),
290 if (res != errSecSuccess) {
291 if (res == errSecAuthFailed) {
292 BOOST_THROW_EXCEPTION(
Error(
"Fail to unlock the keychain"));
295 BOOST_THROW_EXCEPTION(
Error(
"Fail to export private key"));
300 privateKey.
loadPkcs1(CFDataGetBytePtr(exportedKey.get()), CFDataGetLength(exportedKey.get()));
305 BackEndOsx::doHasKey(
const Name& keyName)
const 307 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(
nullptr, keyName.
toUri().c_str(),
308 kCFStringEncodingUTF8);
310 CFReleaser<CFMutableDictionaryRef> attrDict =
311 CFDictionaryCreateMutable(
nullptr, 4, &kCFTypeDictionaryKeyCallBacks,
nullptr);
313 CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
314 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
315 CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
317 CFReleaser<SecKeychainItemRef> itemRef;
319 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&itemRef.get());
322 return res == errSecSuccess;
325 unique_ptr<KeyHandle>
326 BackEndOsx::doGetKeyHandle(
const Name& keyName)
const 328 CFReleaser<SecKeychainItemRef> keyItem;
330 keyItem = m_impl->getKey(keyName);
332 catch (
const std::domain_error&) {
336 return make_unique<KeyHandleOsx>((SecKeyRef)keyItem.get());
339 unique_ptr<KeyHandle>
340 BackEndOsx::doCreateKey(
const Name& identityName,
const KeyParams& params)
356 BOOST_THROW_EXCEPTION(
Tpm::Error(
"Fail to create a key pair: Unsupported key type"));
359 CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(
nullptr, kCFNumberIntType, &keySize);
361 CFReleaser<CFMutableDictionaryRef> attrDict =
362 CFDictionaryCreateMutable(
nullptr, 2, &kCFTypeDictionaryKeyCallBacks,
nullptr);
363 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType,
getAsymKeyType(keyType));
364 CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
368 OSStatus res = SecKeyGeneratePair((CFDictionaryRef)attrDict.get(), &publicKey.get(), &privateKey.get());
370 BOOST_ASSERT(privateKey !=
nullptr);
375 BOOST_ASSERT(privateKey !=
nullptr);
377 if (res != errSecSuccess) {
378 if (res == errSecAuthFailed) {
379 BOOST_THROW_EXCEPTION(
Error(
"Fail to unlock the keychain"));
382 BOOST_THROW_EXCEPTION(
Error(
"Fail to create a key pair"));
386 unique_ptr<KeyHandle> keyHandle = make_unique<KeyHandleOsx>(privateKey.get());
389 SecKeychainAttribute attrs[1];
390 SecKeychainAttributeList attrList = { 0, attrs };
391 std::string keyUri = keyHandle->getKeyName().toUri();
393 attrs[attrList.count].tag = kSecKeyPrintName;
394 attrs[attrList.count].length = keyUri.size();
395 attrs[attrList.count].data =
const_cast<char*
>(keyUri.data());
399 SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)privateKey.get(), &attrList, 0,
nullptr);
400 SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)publicKey.get(), &attrList, 0,
nullptr);
406 BackEndOsx::doDeleteKey(
const Name& keyName)
408 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(
nullptr, keyName.
toUri().c_str(),
409 kCFStringEncodingUTF8);
411 CFReleaser<CFMutableDictionaryRef> searchDict =
412 CFDictionaryCreateMutable(
nullptr, 5, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
414 CFDictionaryAddValue(searchDict.get(), kSecClass, kSecClassKey);
415 CFDictionaryAddValue(searchDict.get(), kSecAttrLabel, keyLabel.get());
416 CFDictionaryAddValue(searchDict.get(), kSecMatchLimit, kSecMatchLimitAll);
417 OSStatus res = SecItemDelete(searchDict.get());
419 if (res != errSecSuccess) {
420 if (res == errSecAuthFailed) {
421 BOOST_THROW_EXCEPTION(
Error(
"Fail to unlock the keychain"));
423 else if (res != errSecItemNotFound) {
424 BOOST_THROW_EXCEPTION(
Error(
"Fail to delete a key pair"));
430 BackEndOsx::doExportKey(
const Name& keyName,
const char* pw,
size_t pwLen)
432 CFReleaser<SecKeychainItemRef> privateKey;
435 privateKey = m_impl->getKey(keyName);
437 catch (
const std::domain_error&) {
438 BOOST_THROW_EXCEPTION(
Tpm::Error(
"Private key does not exist in OSX Keychain"));
441 CFReleaser<CFDataRef> exportedKey;
442 SecItemImportExportKeyParameters keyParams;
443 memset(&keyParams, 0,
sizeof(keyParams));
444 CFReleaser<CFStringRef> passphrase =
445 CFStringCreateWithBytes(0, reinterpret_cast<const uint8_t*>(pw), pwLen, kCFStringEncodingUTF8,
false);
446 keyParams.passphrase = passphrase.get();
447 OSStatus res = SecItemExport(privateKey.get(),
448 kSecFormatWrappedPKCS8,
453 if (res != errSecSuccess) {
454 if (res == errSecAuthFailed) {
455 BOOST_THROW_EXCEPTION(
Error(
"Fail to unlock the keychain"));
458 BOOST_THROW_EXCEPTION(
Error(
"Fail to export private key"));
462 return make_shared<Buffer>(CFDataGetBytePtr(exportedKey.get()), CFDataGetLength(exportedKey.get()));
466 BackEndOsx::doImportKey(
const Name& keyName,
const uint8_t* buf,
size_t size,
467 const char* pw,
size_t pwLen)
469 CFReleaser<CFDataRef> importedKey = CFDataCreateWithBytesNoCopy(
nullptr, buf, size, kCFAllocatorNull);
471 SecExternalFormat externalFormat = kSecFormatWrappedPKCS8;
472 SecExternalItemType externalType = kSecItemTypePrivateKey;
474 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(
nullptr, keyName.
toUri().c_str(),
475 kCFStringEncodingUTF8);
476 CFReleaser<CFStringRef> passphrase =
477 CFStringCreateWithBytes(
nullptr, reinterpret_cast<const uint8_t*>(pw), pwLen, kCFStringEncodingUTF8,
false);
478 CFReleaser<SecAccessRef> access;
479 SecAccessCreate(keyLabel.get(),
nullptr, &access.get());
481 CFArrayRef attributes =
nullptr;
483 const SecItemImportExportKeyParameters keyParams{
484 SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
494 CFReleaser<CFArrayRef> outItems;
495 OSStatus res = SecItemImport(importedKey.get(),
504 if (res != errSecSuccess) {
505 if (res == errSecAuthFailed) {
506 BOOST_THROW_EXCEPTION(
Error(
"Fail to unlock the keychain"));
509 BOOST_THROW_EXCEPTION(
Error(
"Cannot import the private key"));
514 SecKeychainItemRef privateKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
515 SecKeychainAttribute attrs[1];
516 SecKeychainAttributeList attrList = { 0, attrs };
517 std::string keyUri = keyName.
toUri();
519 attrs[attrList.count].tag = kSecKeyPrintName;
520 attrs[attrList.count].length = keyUri.size();
521 attrs[attrList.count].data =
const_cast<char*
>(keyUri.data());
525 res = SecKeychainItemModifyAttributesAndData(privateKey, &attrList, 0,
nullptr);
static ConstBufferPtr decrypt(const KeyRefOsx &key, const uint8_t *cipherText, size_t cipherSize)
bool isTpmLocked() const final
Copyright (c) 2011-2015 Regents of the University of California.
KeyType getKeyType() const
uint32_t getKeySize() const
SecKeychainRef keyChainRef
RSA key, supports sign/verify and encrypt/decrypt operations.
unique_ptr< T > make_unique(Args &&...args)
static CFTypeRef getDigestAlgorithm(DigestAlgorithm digestAlgo)
Catch-all error for security policy errors that don't fit in other categories.
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 TPM is in terminal mode.
void setTerminalMode(bool isTerminal) const final
Set the terminal mode of TPM.
static CFTypeRef getAsymKeyType(KeyType keyType)
static ConstBufferPtr derivePublicKey(const KeyRefOsx &key)
BackEndOsx(const std::string &location="")
Create TPM backed based on macOS KeyChain service.
bool unlockTpm(const char *pw, size_t pwLen) const final
Unlock TPM.
static long getDigestSize(DigestAlgorithm digestAlgo)
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.
static ConstBufferPtr sign(const KeyRefOsx &key, DigestAlgorithm digestAlgorithm, const uint8_t *buf, size_t size)
Sign buf with key using digestAlgorithm.
Base class of key parameters.
SimplePublicKeyParams is a template for public keys with only one parameter: size.
CFReleaser< SecKeychainItemRef > getKey(const Name &keyName)
Get private key reference with name keyName.
shared_ptr< const Buffer > ConstBufferPtr