#include "keyPicker.h"
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <Security/Security.h>
#include <stdexcept>
#include <ctype.h>
#include <clAppUtils/identPicker.h>
#include <vector>
OSStatus getKcItemAttr(
SecKeychainItemRef kcItem,
WhichAttr whichAttr,
CFDataRef *rtnAttr)
{
SecKeychainItemRef attrFromThis;
SecKeychainAttrType attrType = 0;
OSStatus ortn;
bool releaseKcItem = false;
CFTypeID cfId = CFGetTypeID(kcItem);
if(cfId == SecIdentityGetTypeID()) {
ortn = SecIdentityCopyCertificate((SecIdentityRef)kcItem,
(SecCertificateRef *)&attrFromThis);
if(ortn)
cssmPerror("SecIdentityCopyCertificate", ortn);
return ortn;
kcItem = attrFromThis;
releaseKcItem = true;
cfId = SecCertificateGetTypeID();
}
if(cfId == SecCertificateGetTypeID()) {
switch(whichAttr) {
case WA_Hash:
attrType = kSecPublicKeyHashItemAttr;
break;
case WA_PrintName:
attrType = kSecLabelItemAttr;
break;
default:
printf("getKcItemAttr: WhichAttr\n");
return paramErr;
}
}
else if(cfId == SecKeyGetTypeID()) {
switch(whichAttr) {
case WA_Hash:
attrType = kSecKeyLabel;
break;
case WA_PrintName:
attrType = kSecKeyPrintName;
break;
default:
printf("getKcItemAttr: WhichAttr\n");
return paramErr;
}
}
SecKeychainAttributeInfo attrInfo;
attrInfo.count = 1;
attrInfo.tag = &attrType;
attrInfo.format = NULL; SecKeychainAttributeList *attrList = NULL;
ortn = SecKeychainItemCopyAttributesAndData(
kcItem,
&attrInfo,
NULL, &attrList,
NULL, NULL);
if(releaseKcItem) {
CFRelease(kcItem);
}
if(ortn) {
cssmPerror("SecKeychainItemCopyAttributesAndData", ortn);
return paramErr;
}
SecKeychainAttribute *attr = attrList->attr;
*rtnAttr = CFDataCreate(NULL, (UInt8 *)attr->data, attr->length);
SecKeychainItemFreeAttributesAndData(attrList, NULL);
return noErr;
}
class PickerKey
{
public:
PickerKey(SecKeyRef keyRef);
~PickerKey();
bool isUsed() { return mIsUsed;}
void isUsed(bool u) { mIsUsed = u; }
bool isPrivate() { return mIsPrivate; }
CFDataRef getPrintName() { return mPrintName; }
CFDataRef getPubKeyHash() { return mPubKeyHash; }
SecKeyRef keyRef() { return mKeyRef; }
PickerKey *partnerKey() { return mPartner; }
void partnerKey(PickerKey *pk) { mPartner = pk; }
char *kcFile() { return mKcFile; }
private:
SecKeyRef mKeyRef;
CFDataRef mPrintName;
CFDataRef mPubKeyHash;
bool mIsPrivate; bool mIsUsed; PickerKey *mPartner; char *mKcFile; };
PickerKey::PickerKey(SecKeyRef keyRef)
: mKeyRef(NULL),
mPrintName(NULL),
mPubKeyHash(NULL),
mIsPrivate(false),
mIsUsed(false),
mPartner(NULL),
mKcFile(NULL)
{
if(CFGetTypeID(keyRef) != SecKeyGetTypeID()) {
throw std::invalid_argument("not a key");
}
OSStatus ortn = getKcItemAttr((SecKeychainItemRef)keyRef, WA_Hash, &mPubKeyHash);
if(ortn) {
throw std::invalid_argument("pub key hash not available");
}
ortn = getKcItemAttr((SecKeychainItemRef)keyRef, WA_PrintName, &mPrintName);
if(ortn) {
throw std::invalid_argument("pub key hash not available");
}
const CSSM_KEY *cssmKey;
ortn = SecKeyGetCSSMKey(keyRef, &cssmKey);
if(ortn) {
cssmPerror("SecKeyGetCSSMKey", ortn);
throw std::invalid_argument("SecKeyGetCSSMKey error");
}
if(cssmKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) {
mIsPrivate = true;
}
SecKeychainRef kcRef;
ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)keyRef, &kcRef);
if(ortn) {
cssmPerror("SecKeychainItemCopyKeychain", ortn);
mKcFile = strdup("Unnamed keychain");
}
else {
mKcFile = kcFileName(kcRef);
}
mKeyRef = keyRef;
CFRetain(mKeyRef);
}
PickerKey::~PickerKey()
{
if(mKeyRef) {
CFRelease(mKeyRef);
}
if(mPubKeyHash) {
CFRelease(mPubKeyHash);
}
if(mPrintName) {
CFRelease(mPrintName);
}
if(mKcFile) {
free(mKcFile);
}
}
typedef std::vector<PickerKey *> KeyVector;
static void getPickerKeys(
SecKeychainRef kcRef,
SecItemClass itemClass, KeyVector &keyVector)
{
SecKeychainSearchRef srchRef = NULL;
SecKeychainItemRef kcItem;
OSStatus ortn = SecKeychainSearchCreateFromAttributes(kcRef,
itemClass,
NULL, &srchRef);
if(ortn) {
cssmPerror("SecKeychainSearchCreateFromAttributes", ortn);
return;
}
do {
ortn = SecKeychainSearchCopyNext(srchRef, &kcItem);
if(ortn) {
break;
}
try {
PickerKey *pickerKey = new PickerKey((SecKeyRef)kcItem);
keyVector.push_back(pickerKey);
}
catch(...) {
printf("**** key item that failed PickerKey construct ***\n");
}
} while(ortn == noErr);
CFRelease(srchRef);
}
static void printCfData(CFDataRef cfd)
{
CFIndex len = CFDataGetLength(cfd);
const UInt8 *cp = CFDataGetBytePtr(cfd);
for(CFIndex dex=0; dex<len; dex++) {
char c = cp[dex];
if(isprint(c)) {
putchar(c);
}
else {
printf(".%02X.", c);
}
}
}
OSStatus keyPicker(
SecKeychainRef kcRef, SecKeyRef *pubKey, SecKeyRef *privKey) {
std::vector<PickerKey *> privKeys;
std::vector<PickerKey *> pubKeys;
getPickerKeys(kcRef, CSSM_DL_DB_RECORD_PRIVATE_KEY, privKeys);
getPickerKeys(kcRef, CSSM_DL_DB_RECORD_PUBLIC_KEY, pubKeys);
int numPairs = 0;
unsigned numPrivKeys = privKeys.size();
unsigned numPubKeys = pubKeys.size();
for(unsigned privDex=0; privDex<numPrivKeys; privDex++) {
PickerKey *privPk = privKeys[privDex];
CFDataRef privHash = privPk->getPubKeyHash();
for(unsigned pubDex=0; pubDex<numPubKeys; pubDex++) {
PickerKey *pubPk = pubKeys[pubDex];
if(pubPk->isUsed()) {
continue;
}
if(!CFEqual(privHash, pubPk->getPubKeyHash())) {
continue;
}
pubPk->partnerKey(privPk);
privPk->partnerKey(pubPk);
pubPk->isUsed(true);
privPk->isUsed(true);
printf("[%d] privKey : ", numPairs); printCfData(privPk->getPrintName()); printf("\n");
printf(" pubKey : "); printCfData(pubPk->getPrintName());printf("\n");
printf(" keychain : %s\n", privPk->kcFile());
numPairs++;
}
}
if(numPairs == 0) {
printf("*** keyPicker: no key pairs found.\n");
return paramErr;
}
OSStatus ortn = noErr;
int ires;
while(1) {
fpurge(stdin);
printf("\nEnter key pair number or CR to quit : ");
fflush(stdout);
char resp[64];
getString(resp, sizeof(resp));
if(resp[0] == '\0') {
ortn = CSSMERR_CSSM_USER_CANCELED;
break;
}
ires = atoi(resp);
if((ires < 0) || (ires >= numPairs)) {
printf("***Invalid entry. Type a number between 0 and %d\n", numPairs-1);
continue;
}
break;
}
if(ortn == noErr) {
int goodOnes = 0;
for(unsigned privDex=0; privDex<numPrivKeys; privDex++) {
PickerKey *privPk = privKeys[privDex];
if(!privPk->isUsed()) {
continue;
}
if(goodOnes == ires) {
*privKey = privPk->keyRef();
*pubKey = privPk->partnerKey()->keyRef();
}
goodOnes++;
}
}
for(unsigned privDex=0; privDex<numPrivKeys; privDex++) {
delete privKeys[privDex];
}
for(unsigned pubDex=0; pubDex<numPubKeys; pubDex++) {
delete pubKeys[pubDex];
}
return ortn;
}