]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTests/clxutils/clAppUtils/keyPicker.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTests / clxutils / clAppUtils / keyPicker.cpp
diff --git a/SecurityTests/clxutils/clAppUtils/keyPicker.cpp b/SecurityTests/clxutils/clAppUtils/keyPicker.cpp
new file mode 100644 (file)
index 0000000..a8bbf5f
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2004-2006 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * keyPicker.cpp - select a key pair from a keychain
+ */
+#include "keyPicker.h"
+#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
+#include <Security/Security.h>
+#include <stdexcept>
+#include <ctype.h>
+#include <clAppUtils/identPicker.h>            /* for kcFileName() */
+#include <vector>
+
+/*
+ * Obtain either public key hash or PrintName for a given SecKeychainItem. Works on public keys,
+ * private keys, identities, and certs. Caller must release the returned result. 
+ */
+OSStatus getKcItemAttr(
+       SecKeychainItemRef kcItem,
+       WhichAttr whichAttr,
+       CFDataRef *rtnAttr)                     /* RETURNED */
+{
+       /* main job is to figure out which attrType to ask for, and from what Sec item */
+       SecKeychainItemRef attrFromThis;
+       SecKeychainAttrType     attrType = 0;
+       OSStatus ortn;
+       bool releaseKcItem = false;
+       
+       CFTypeID cfId = CFGetTypeID(kcItem);
+       if(cfId == SecIdentityGetTypeID()) {
+               /* switch over to cert */
+               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,                   // itemClass
+               &attrList, 
+               NULL,                   // don't need the data
+               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 representing one key in the keychain.
+ */
+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;             // private/public key
+       bool            mIsUsed;                // has been spoken for 
+       PickerKey       *mPartner;              // other member of public/private pair
+       char            *mKcFile;               // file name of keychain this lives on 
+};
+
+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) {
+               /* should never happen */
+               cssmPerror("SecKeyGetCSSMKey", ortn);
+               throw std::invalid_argument("SecKeyGetCSSMKey error");
+       }
+       if(cssmKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) {
+               mIsPrivate = true;
+       }
+       
+       /* stash name of the keychain this lives on */
+       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;
+
+/* 
+ * add PickerKey objects of specified type to a KeyVector. 
+ */
+static void getPickerKeys(
+       SecKeychainRef  kcRef,
+       SecItemClass    itemClass,      // actually CSSM_DL_DB_RECORD_{PRIVATE,PRIVATE}_KEY for now
+       KeyVector               &keyVector)
+{
+       SecKeychainSearchRef    srchRef = NULL;
+       SecKeychainItemRef              kcItem;
+       
+       OSStatus ortn = SecKeychainSearchCreateFromAttributes(kcRef,
+               itemClass,
+               NULL,                   // any attrs
+               &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");
+                       /* but keep going */
+               }
+       } while(ortn == noErr);
+       CFRelease(srchRef);
+}
+
+/*
+ * Print contents of a CFData assuming it's printable 
+ */
+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,          // NULL means the default list
+       SecKeyRef               *pubKey,        // RETURNED
+       SecKeyRef               *privKey)   // RETURNED
+{
+
+       /* First create a arrays of all of the keys, parsed and ready for use */
+       
+       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);
+       
+       /* now interate thru private keys, looking for a partner for each one */
+       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()) {
+                               /* already spoken for */
+                               continue;
+                       }
+                       if(!CFEqual(privHash, pubPk->getPubKeyHash())) {  
+                               /* public key hashes don't match */
+                               continue;
+                       }
+                       
+                       /* got a match */
+                       pubPk->partnerKey(privPk);
+                       privPk->partnerKey(pubPk);
+                       pubPk->isUsed(true);
+                       privPk->isUsed(true);
+                       
+                       /* display */
+                       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) {
+               /* find the ires'th partnered private key */
+               int goodOnes = 0;
+               for(unsigned privDex=0; privDex<numPrivKeys; privDex++) {
+                       PickerKey *privPk = privKeys[privDex];
+                       if(!privPk->isUsed()) {
+                               continue;
+                       }
+                       if(goodOnes == ires) {
+                               /* this is it */
+                               *privKey = privPk->keyRef();
+                               *pubKey = privPk->partnerKey()->keyRef();
+                       }
+                       goodOnes++;
+               }
+       }
+       
+       /* clean out PickerKey arrays */
+       for(unsigned privDex=0; privDex<numPrivKeys; privDex++) {
+               delete privKeys[privDex];
+       }
+       for(unsigned pubDex=0; pubDex<numPubKeys; pubDex++) {
+               delete pubKeys[pubDex];
+       }
+       return ortn;
+}
+