#include "TokenIDHelper.h"
#include <Security/SecKeychain.h>
#include <Security/SecKeychainPriv.h>
#include <Security/SecCertificate.h>
#include <Security/SecKey.h>
#include <security_utilities/cfutilities.h>
#include <security_utilities/errors.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
static void extract_certificate_from_identity(const void *value, void *context);
static bool encryptionEnabled(SecKeyRef privateKeyRef);
static OSStatus findCertificatePublicKeyHash(SecCertificateRef certificate, CFDataRef *label);
int findFirstEncryptionPublicKeyOnToken(SecKeyRef *publicKey, SecKeychainRef *keychainRef, CFDataRef *label)
{
if (!publicKey || !keychainRef)
return paramErr;
OSStatus status = noErr;
CFArrayRef identityArray = NULL;
SecKeyRef tmpKeyRef = NULL;
SecCertificateRef certificate = NULL;
SecKeychainRef tmpKeychainRef = NULL;
try
{
status = findEncryptionIdentities((CFTypeRef *)&identityArray);
if (status)
MacOSError::throwMe(status);
if (!identityArray ||
(CFGetTypeID(identityArray)!=CFArrayGetTypeID()) ||
(CFArrayGetCount(identityArray)==0))
MacOSError::throwMe(paramErr);
CFTypeRef tmpref = CFArrayGetValueAtIndex(identityArray, 0);
if (CFGetTypeID(tmpref)!=SecIdentityGetTypeID())
MacOSError::throwMe(paramErr);
status = SecIdentityCopyCertificate(SecIdentityRef(tmpref), &certificate);
if (status)
MacOSError::throwMe(status);
if (!certificate)
MacOSError::throwMe(errKCItemNotFound);
status = findCertificatePublicKeyHash(certificate, label);
if (status)
MacOSError::throwMe(status);
status = SecKeychainItemCopyKeychain(SecKeychainItemRef(certificate), &tmpKeychainRef);
if (status)
MacOSError::throwMe(status);
status = SecCertificateCopyPublicKey(certificate, &tmpKeyRef);
if (status)
MacOSError::throwMe(status);
*publicKey = tmpKeyRef;
*keychainRef = tmpKeychainRef;
}
catch (const MacOSError &err)
{
status = err.osStatus();
cssmPerror("findFirstEncryptionPublicKeyOnToken", status);
}
catch (...)
{
fprintf(stderr, "findFirstEncryptionPublicKeyOnToken: unknown exception\n");
status = errKCItemNotFound;
}
if (status)
{
if (identityArray)
CFRelease(identityArray);
if (certificate)
CFRelease(certificate);
}
if (identityArray)
CFRelease(identityArray);
if (certificate)
CFRelease(certificate);
return status;
}
OSStatus findCertificatePublicKeyHash(SecCertificateRef certificate, CFDataRef *label)
{
UInt32 tag[1] = { kSecPublicKeyHashItemAttr }; UInt32 format[1] = { CSSM_DB_ATTRIBUTE_FORMAT_BLOB };
SecKeychainAttributeInfo info = { 1, tag, format };
SecKeychainAttributeList *attrList = NULL;
OSStatus status = SecKeychainItemCopyAttributesAndData(SecKeychainItemRef(certificate), &info, NULL, &attrList, 0, NULL);
if (status || !attrList || !attrList->count)
return status;
const uint32_t index = 0;
if (attrList->attr[index].tag == kSecPublicKeyHashItemAttr)
*label = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)attrList->attr[index].data, attrList->attr[index].length);
SecKeychainItemFreeAttributesAndData(attrList, NULL);
return noErr;
}
int findEncryptionIdentities(CFTypeRef *identityOrArray)
{
OSStatus status = noErr;
CFArrayRef searchList = NULL;
CFMutableArrayRef idArray = NULL;
status = SecKeychainCopyDomainSearchList(kSecPreferencesDomainDynamic, &searchList);
if (status)
return status;
uint32_t count = searchList ? CFArrayGetCount(searchList) : 0;
if (!count)
return errSecNoSuchKeychain;
uint32_t keyuse = 0;
SecIdentitySearchRef srchRef = NULL;
status = SecIdentitySearchCreate(searchList, keyuse, &srchRef);
if (status)
return status;
while (!status)
{
SecIdentityRef identity = NULL;
status = SecIdentitySearchCopyNext(srchRef, &identity);
if (status == errSecItemNotFound) break;
if (status)
return status;
SecKeyRef privateKeyRef = NULL;
status = SecIdentityCopyPrivateKey(identity, &privateKeyRef);
if (status)
continue;
bool canEncrypt = encryptionEnabled(privateKeyRef);
CFRelease(privateKeyRef);
if (!canEncrypt)
continue;
if (!idArray)
idArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(idArray, identity);
}
if ((status == noErr || status == errSecItemNotFound) && idArray && CFArrayGetCount(idArray))
{
if (idArray)
{
*identityOrArray = idArray;
::CFRetain(*identityOrArray);
}
status = noErr;
}
else
if (idArray)
CFRelease(idArray);
return status;
}
int unlockToken(const char *password)
{
OSStatus status = noErr;
if (!password)
return paramErr;
CFArrayRef searchList = NULL;
status = SecKeychainCopyDomainSearchList(kSecPreferencesDomainDynamic, &searchList);
if (status)
return status;
uint32_t count = searchList ? CFArrayGetCount(searchList) : 0;
if (count)
{
SecKeychainRef keychainRef = (SecKeychainRef)CFArrayGetValueAtIndex(searchList, 0); status = SecKeychainUnlock(keychainRef, strlen(password), password, 1);
if (keychainRef)
CFRelease(keychainRef);
}
else
status = errSecNoSuchKeychain;
if (searchList)
CFRelease(searchList);
return status;
}
void extractCertificatesFromIdentities(CFTypeRef identityOrArray, CFArrayRef *certificateArrayOut)
{
if (!identityOrArray || !certificateArrayOut)
return;
CFIndex cnt = (CFGetTypeID(identityOrArray)==CFArrayGetTypeID())?CFArrayGetCount((CFArrayRef)identityOrArray):1;
CFMutableArrayRef certificateArray = CFArrayCreateMutable(kCFAllocatorDefault, cnt, &kCFTypeArrayCallBacks);
if (CFGetTypeID(identityOrArray)==CFArrayGetTypeID())
CFArrayApplyFunction((CFArrayRef)identityOrArray, CFRangeMake(0, cnt),
extract_certificate_from_identity,
certificateArray);
else
extract_certificate_from_identity(identityOrArray, certificateArray);
*certificateArrayOut = certificateArray;
}
void extract_certificate_from_identity(const void *value, void *context)
{
if (!context || !value)
return;
CSSM_DATA certData = {0,};
SecCertificateRef certificateRef;
OSStatus status = SecIdentityCopyCertificate((SecIdentityRef)value, &certificateRef);
if (!status)
{
status = SecCertificateGetData(certificateRef, &certData);
CFRelease(certificateRef);
if (!status)
{
CFDataRef cert = CFDataCreate(kCFAllocatorDefault, (UInt8 *)certData.Data, certData.Length);
CFArrayAppendValue((CFMutableArrayRef)context, cert);
CFRelease(cert);
if (certData.Data)
free(certData.Data);
}
}
}
bool encryptionEnabled(SecKeyRef privateKeyRef)
{
UInt32 tag[] = { kSecKeyEncrypt, kSecKeyDecrypt, kSecKeyDerive, kSecKeyWrap, kSecKeyUnwrap };
UInt32 format[] = { CSSM_DB_ATTRIBUTE_FORMAT_UINT32, CSSM_DB_ATTRIBUTE_FORMAT_UINT32,
CSSM_DB_ATTRIBUTE_FORMAT_UINT32, CSSM_DB_ATTRIBUTE_FORMAT_UINT32, CSSM_DB_ATTRIBUTE_FORMAT_UINT32};
SecKeychainAttributeInfo info = { 5, tag, format };
SecKeychainAttributeList *attrList = NULL;
OSStatus status = SecKeychainItemCopyAttributesAndData((SecKeychainItemRef)privateKeyRef, &info, NULL, &attrList, 0, NULL);
if (status || !attrList)
return false;
bool canEncrypt = false;
for (uint32_t index = 0; index < attrList->count; ++index)
{
if (attrList->attr[index].length != sizeof(uint32_t) || !attrList->attr[index].data ||
0 == *(uint32_t*)attrList->attr[index].data)
continue;
canEncrypt = true;
break;
}
status = SecKeychainItemFreeAttributesAndData(attrList, NULL);
return canEncrypt;
}