]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_keychain/lib/SecImportExportUtils.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_keychain / lib / SecImportExportUtils.cpp
diff --git a/Security/libsecurity_keychain/lib/SecImportExportUtils.cpp b/Security/libsecurity_keychain/lib/SecImportExportUtils.cpp
new file mode 100644 (file)
index 0000000..e0f1776
--- /dev/null
@@ -0,0 +1,972 @@
+/*
+ * Copyright (c) 2004,2011-2014 Apple 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@
+ *
+ * SecImportExportUtils.cpp - misc. utilities for import/export module
+ */
+
+#include "SecImportExportUtils.h"
+#include "SecImportExportAgg.h"
+#include "SecImportExportCrypto.h"
+#include "SecIdentityPriv.h"
+#include "SecItem.h"
+#include <security_cdsa_utils/cuCdsaUtils.h>
+#include <Security/SecBase.h>
+#pragma mark --- Debug support ---
+
+#ifndef NDEBUG
+
+const char *impExpExtFormatStr(
+       SecExternalFormat format)
+{
+       switch(format) {
+               case kSecFormatUnknown:                 return "kSecFormatUnknown";
+               case kSecFormatOpenSSL:                 return "kSecFormatOpenSSL";
+               case kSecFormatSSH:                             return "kSecFormatSSH";
+               case kSecFormatBSAFE:                   return "kSecFormatBSAFE";
+               case kSecFormatRawKey:                  return "kSecFormatRawKey";
+               case kSecFormatWrappedPKCS8:    return "kSecFormatWrappedPKCS8";
+               case kSecFormatWrappedOpenSSL:  return "kSecFormatWrappedOpenSSL";
+               case kSecFormatWrappedSSH:              return "kSecFormatWrappedSSH";
+               case kSecFormatWrappedLSH:              return "kSecFormatWrappedLSH";
+               case kSecFormatX509Cert:                return "kSecFormatX509Cert";
+               case kSecFormatPEMSequence:             return "kSecFormatPEMSequence";
+               case kSecFormatPKCS7:                   return "kSecFormatPKCS7";
+               case kSecFormatPKCS12:                  return "kSecFormatPKCS12";
+               case kSecFormatNetscapeCertSequence:  return "kSecFormatNetscapeCertSequence";
+               default:                                                return "UNKNOWN FORMAT ENUM";
+       }
+}
+
+const char *impExpExtItemTypeStr(
+       SecExternalItemType itemType)
+{
+       switch(itemType) {
+               case kSecItemTypeUnknown:               return "kSecItemTypeUnknown";
+               case kSecItemTypePrivateKey:    return "kSecItemTypePrivateKey";
+               case kSecItemTypePublicKey:             return "kSecItemTypePublicKey";
+               case kSecItemTypeSessionKey:    return "kSecItemTypeSessionKey";
+               case kSecItemTypeCertificate:   return "kSecItemTypeCertificate";
+               case kSecItemTypeAggregate:             return "kSecItemTypeAggregate";
+               default:                                                return "UNKNOWN ITEM TYPE ENUM";
+       }
+}
+#endif  /* NDEBUG */
+
+/*
+ * Parse file extension and attempt to map it to format and type. Returns true
+ * on success.
+ */
+bool impExpImportParseFileExten(
+       CFStringRef                     fstr,
+       SecExternalFormat   *inputFormat,   // RETURNED
+       SecExternalItemType     *itemType)              // RETURNED
+{
+       if(fstr == NULL) {
+               /* nothing to work with */
+               return false;
+       }
+       if(CFStringHasSuffix(fstr, CFSTR(".cer")) ||
+          CFStringHasSuffix(fstr, CFSTR(".crt"))) {
+               *inputFormat = kSecFormatX509Cert;
+               *itemType = kSecItemTypeCertificate;
+               SecImpInferDbg("Inferring kSecFormatX509Cert from file name");
+               return true;
+       }
+       if(CFStringHasSuffix(fstr, CFSTR(".p12")) ||
+          CFStringHasSuffix(fstr, CFSTR(".pfx"))) {
+               *inputFormat = kSecFormatPKCS12;
+               *itemType = kSecItemTypeAggregate;
+               SecImpInferDbg("Inferring kSecFormatPKCS12 from file name");
+               return true;
+       }
+
+       /* Get extension, look for key indicators as substrings */
+       CFURLRef url = CFURLCreateWithString(NULL, fstr, NULL);
+       if(url == NULL) {
+               SecImpInferDbg("impExpImportParseFileExten: error creating URL");
+               return false;
+       }
+       CFStringRef exten = CFURLCopyPathExtension(url);
+       CFRelease(url);
+       if(exten == NULL) {
+               /* no extension, app probably passed in only an extension */
+               exten = fstr;
+               CFRetain(exten);
+       }
+       bool ortn = false;
+       CFRange cfr;
+       cfr = CFStringFind(exten, CFSTR("p7"), kCFCompareCaseInsensitive);
+       if(cfr.length != 0) {
+               *inputFormat = kSecFormatPKCS7;
+               *itemType = kSecItemTypeAggregate;
+               SecImpInferDbg("Inferring kSecFormatPKCS7 from file name");
+               ortn = true;
+       }
+       if(!ortn) {
+               cfr = CFStringFind(exten, CFSTR("p8"), kCFCompareCaseInsensitive);
+               if(cfr.length != 0) {
+                       *inputFormat = kSecFormatWrappedPKCS8;
+                       *itemType = kSecItemTypePrivateKey;
+                       SecImpInferDbg("Inferring kSecFormatPKCS8 from file name");
+                       ortn = true;
+               }
+       }
+       CFRelease(exten);
+       return ortn;
+}
+
+/* do a [NSString stringByDeletingPathExtension] equivalent */
+CFStringRef impExpImportDeleteExtension(
+       CFStringRef                     fileStr)
+{
+       CFDataRef fileStrData = CFStringCreateExternalRepresentation(NULL, fileStr,
+               kCFStringEncodingUTF8, 0);
+       if(fileStrData == NULL) {
+               return NULL;
+       }
+
+       CFURLRef urlRef = CFURLCreateFromFileSystemRepresentation(NULL,
+               CFDataGetBytePtr(fileStrData), CFDataGetLength(fileStrData), false);
+       if(urlRef == NULL) {
+               CFRelease(fileStrData);
+               return NULL;
+       }
+       CFURLRef rtnUrl = CFURLCreateCopyDeletingPathExtension(NULL, urlRef);
+       CFStringRef rtnStr = NULL;
+       CFRelease(urlRef);
+       if(rtnUrl) {
+               rtnStr = CFURLGetString(rtnUrl);
+               CFRetain(rtnStr);
+               CFRelease(rtnUrl);
+       }
+       CFRelease(fileStrData);
+       return rtnStr;
+}
+
+#pragma mark --- mapping of external format to CDSA formats ---
+
+/*
+ * For the record, here is the mapping of SecExternalFormat, algorithm, and key
+ * class to CSSM-style key format (CSSM_KEYBLOB_FORMAT -
+ * CSSM_KEYBLOB_RAW_FORMAT_X509, etc). The entries in the table are the
+ * last component of a CSSM_KEYBLOB_FORMAT. Format kSecFormatUnknown means
+ * "default for specified class and algorithm", which is currently the
+ * same as kSecFormatOpenSSL.
+ *
+ *                                                                   algorithm/class
+ *                                                   RSA                           DSA                            DH
+ *                                             ----------------  ----------------  ----------------
+ * SecExternalFormat    priv      pub     priv      pub         priv      pub
+ * -----------------   -------  -------  -------  -------  -------  -------
+ * kSecFormatOpenSSL    PKCS1    X509    OPENSSL   X509     PKCS3    X509
+ * kSecFormatBSAFE       PKCS8    PKCS1   FIPS186   FIPS186  PKCS8    not supported
+ * kSecFormatUnknown    PKCS1    X509    OPENSSL   X509     PKCS3    X509
+ * kSecFormatSSH                 SSH      SSH      n/s     n/s       n/s     n/s
+ * kSecFormatSSHv2        n/s     SSH2      n/s     SSH2      n/s     n/s
+ *
+ * The only external format supported for ECDSA and ECDH keys is kSecFormatOpenSSL,
+ * which translates to OPENSSL for private keys and X509 for public keys.
+ */
+
+/* Arrays expressing the above table. */
+
+/* l.s. dimension is pub/priv for one alg */
+typedef struct {
+       CSSM_KEYBLOB_FORMAT priv;
+       CSSM_KEYBLOB_FORMAT pub;
+} algForms;
+
+/*
+ * indices into array of algForms defining all algs' formats for a given
+ * SecExternalFormat
+ */
+#define SIE_ALG_RSA            0
+#define SIE_ALG_DSA            1
+#define SIE_ALG_DH             2
+#define SIE_ALG_ECDSA  3
+#define SIE_ALG_LAST   SIE_ALG_ECDSA
+#define SIE_NUM_ALGS   (SIE_ALG_LAST + 1)
+
+/* kSecFormatOpenSSL */
+static algForms opensslAlgForms[SIE_NUM_ALGS] =
+{
+       { CSSM_KEYBLOB_RAW_FORMAT_PKCS1,        CSSM_KEYBLOB_RAW_FORMAT_X509 },         // RSA
+       { CSSM_KEYBLOB_RAW_FORMAT_OPENSSL,  CSSM_KEYBLOB_RAW_FORMAT_X509 },             // DSA
+       { CSSM_KEYBLOB_RAW_FORMAT_PKCS3,        CSSM_KEYBLOB_RAW_FORMAT_X509 },         // D-H
+       { CSSM_KEYBLOB_RAW_FORMAT_OPENSSL,      CSSM_KEYBLOB_RAW_FORMAT_X509 },         // ECDSA
+};
+
+/* kSecFormatBSAFE */
+static algForms bsafeAlgForms[SIE_NUM_ALGS] =
+{
+       { CSSM_KEYBLOB_RAW_FORMAT_PKCS8,        CSSM_KEYBLOB_RAW_FORMAT_PKCS1 },        // RSA
+       { CSSM_KEYBLOB_RAW_FORMAT_FIPS186,  CSSM_KEYBLOB_RAW_FORMAT_FIPS186 },  // DSA
+       { CSSM_KEYBLOB_RAW_FORMAT_PKCS8,        CSSM_KEYBLOB_RAW_FORMAT_NONE },         // D-H
+       { CSSM_KEYBLOB_RAW_FORMAT_NONE,         CSSM_KEYBLOB_RAW_FORMAT_NONE },         // ECDSA not supported
+};
+
+/* kSecFormatSSH (v1) */
+static algForms ssh1AlgForms[SIE_NUM_ALGS] =
+{
+       { CSSM_KEYBLOB_RAW_FORMAT_OPENSSH,      CSSM_KEYBLOB_RAW_FORMAT_OPENSSH },      // RSA only
+       { CSSM_KEYBLOB_RAW_FORMAT_NONE,         CSSM_KEYBLOB_RAW_FORMAT_NONE },         // DSA not supported
+       { CSSM_KEYBLOB_RAW_FORMAT_NONE,         CSSM_KEYBLOB_RAW_FORMAT_NONE },         // D-H not supported
+       { CSSM_KEYBLOB_RAW_FORMAT_NONE,         CSSM_KEYBLOB_RAW_FORMAT_NONE },         // ECDSA not supported
+};
+
+/* kSecFormatSSHv2 */
+static algForms ssh2AlgForms[SIE_NUM_ALGS] =
+{
+       { CSSM_KEYBLOB_RAW_FORMAT_NONE,         CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2 },     // RSA - public only
+       { CSSM_KEYBLOB_RAW_FORMAT_NONE,         CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2 },     // DSA - public only
+       { CSSM_KEYBLOB_RAW_FORMAT_NONE,         CSSM_KEYBLOB_RAW_FORMAT_NONE },         // D-H not supported
+       { CSSM_KEYBLOB_RAW_FORMAT_NONE,         CSSM_KEYBLOB_RAW_FORMAT_NONE },         // ECDSA not supported
+};
+
+/*
+ * This routine performs a lookup into the above 3-dimensional array to
+ * map {algorithm, class, SecExternalFormat} to a CSSM_KEYBLOB_FORMAT.
+ * Returns errSecUnsupportedFormat in the rare appropriate case.
+ */
+OSStatus impExpKeyForm(
+       SecExternalFormat               externForm,
+       SecExternalItemType             itemType,
+       CSSM_ALGORITHMS                 alg,
+       CSSM_KEYBLOB_FORMAT             *cssmForm,              // RETURNED
+       CSSM_KEYCLASS                   *cssmClass)             // RETRUNED
+{
+       if(itemType == kSecItemTypeSessionKey) {
+               /* special trivial case */
+               /* FIXME ensure caller hasn't specified bogus format */
+               *cssmForm = CSSM_KEYBLOB_RAW_FORMAT_NONE;
+               *cssmClass = CSSM_KEYCLASS_SESSION_KEY;
+               return errSecSuccess;
+       }
+       if(externForm == kSecFormatUnknown) {
+               /* default is openssl */
+               externForm = kSecFormatOpenSSL;
+       }
+
+       unsigned algDex;
+       switch(alg) {
+               case CSSM_ALGID_RSA:
+                       algDex = SIE_ALG_RSA;
+                       break;
+               case CSSM_ALGID_DSA:
+                       algDex = SIE_ALG_DSA;
+                       break;
+               case CSSM_ALGID_DH:
+                       algDex = SIE_ALG_DH;
+                       break;
+               case CSSM_ALGID_ECDSA:
+                       algDex = SIE_ALG_ECDSA;
+                       break;
+               default:
+                       return CSSMERR_CSP_INVALID_ALGORITHM;
+       }
+       const algForms *forms;
+       switch(externForm) {
+               case kSecFormatOpenSSL:
+                       forms = opensslAlgForms;
+                       break;
+               case kSecFormatBSAFE:
+                       forms = bsafeAlgForms;
+                       break;
+               case kSecFormatSSH:
+                       forms = ssh1AlgForms;
+                       break;
+               case kSecFormatSSHv2:
+                       forms = ssh2AlgForms;
+                       break;
+               default:
+                       return errSecUnsupportedFormat;
+       }
+       CSSM_KEYBLOB_FORMAT form = CSSM_KEYBLOB_RAW_FORMAT_NONE;
+       switch(itemType) {
+               case kSecItemTypePrivateKey:
+                       form = forms[algDex].priv;
+                       *cssmClass = CSSM_KEYCLASS_PRIVATE_KEY;
+                       break;
+               case kSecItemTypePublicKey:
+                       form = forms[algDex].pub;
+                       *cssmClass = CSSM_KEYCLASS_PUBLIC_KEY;
+                       break;
+               default:
+                       return errSecUnsupportedFormat;
+       }
+       if(form == CSSM_KEYBLOB_RAW_FORMAT_NONE) {
+               /* not in the tables - abort */
+               return errSecUnsupportedFormat;
+       }
+       else {
+               *cssmForm = form;
+               return errSecSuccess;
+       }
+}
+
+/*
+ * Given a raw key blob and zero to three known parameters (type, format,
+ * algorithm), figure out all parameters. Used for private and public keys.
+ */
+static bool impExpGuessKeyParams(
+       CFDataRef                               keyData,
+       SecExternalFormat               *externForm,            // IN/OUT
+       SecExternalItemType             *itemType,                      // IN/OUT
+       CSSM_ALGORITHMS                 *keyAlg)                        // IN/OUT
+{
+       /* CSSM alg list: RSA, DSA, DH, ECDSA */
+       CSSM_ALGORITHMS minAlg           = CSSM_ALGID_RSA;
+       CSSM_ALGORITHMS maxAlg           = CSSM_ALGID_ECDSA;
+       SecExternalFormat minForm    = kSecFormatOpenSSL;               // then SSH, BSAFE, then...
+       SecExternalFormat maxForm    = kSecFormatSSHv2;
+       SecExternalItemType minType  = kSecItemTypePrivateKey;  // just two
+       SecExternalItemType maxType  = kSecItemTypePublicKey;
+
+    
+    if(keyData == NULL || CFDataGetLength(keyData) == 0){
+                MacOSError::throwMe(errSecUnsupportedKeySize);
+    }
+    
+       switch(*externForm) {
+               case kSecFormatUnknown:
+                       break;                                                          // run through all formats
+               case kSecFormatOpenSSL:
+               case kSecFormatSSH:
+               case kSecFormatSSHv2:
+               case kSecFormatBSAFE:
+                       minForm = maxForm = *externForm;        // just test this one
+                       break;
+               default:
+                       return false;
+       }
+       switch(*itemType) {
+               case kSecItemTypeUnknown:
+                       break;
+               case kSecItemTypePrivateKey:
+               case kSecItemTypePublicKey:
+                       minType = maxType = *itemType;
+                       break;
+               default:
+                       return false;
+       }
+       switch(*keyAlg) {
+               case CSSM_ALGID_NONE:
+                       break;
+               case CSSM_ALGID_RSA:
+               case CSSM_ALGID_DSA:
+               case CSSM_ALGID_DH:
+               case CSSM_ALGID_ECDSA:
+                       minAlg = maxAlg = *keyAlg;
+                       break;
+               default:
+                       return false;
+       }
+
+       CSSM_ALGORITHMS theAlg;
+       SecExternalFormat theForm;
+       SecExternalItemType theType;
+       CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_TRUE);
+       if(cspHand == 0) {
+               return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
+       }
+
+       /*
+        * Iterate through all set of enabled {alg, type, format}.
+        * We do not assume that any of the enums are sequential hence this
+        * odd iteration algorithm....
+        */
+       bool ourRtn = false;
+       for(theAlg=minAlg; ; ) {
+               for(theForm=minForm; ; ) {
+                       for(theType=minType; ; ) {
+
+                               /* do super lightweight null unwrap to parse */
+                               OSStatus ortn = impExpImportRawKey(keyData,
+                                       theForm, theType, theAlg,
+                                       NULL,           // no keychain
+                                       cspHand,
+                                       0,                      // no flags
+                                       NULL,           // no key params
+                                       NULL,           // no printName
+                                       NULL);          // no returned items
+                               if(ortn == errSecSuccess) {
+                                       *externForm = theForm;
+                                       *itemType = theType;
+                                       *keyAlg = theAlg;
+                                       ourRtn = true;
+                                       goto done;
+                               }
+
+                               /* next type or break if we're done */
+                               if(theType == maxType) {
+                                       break;
+                               }
+                               else switch(theType) {
+                                       case kSecItemTypePrivateKey:
+                                               theType = kSecItemTypePublicKey;
+                                               break;
+                                       default:
+                                               assert(0);
+                                               ourRtn = false;
+                                               goto done;
+                               }
+                       }   /* for each class/type */
+
+                       /* next format or break if we're done */
+                       if(theForm == maxForm) {
+                               break;
+                       }
+                       else switch(theForm) {
+                               case kSecFormatOpenSSL:
+                                       theForm = kSecFormatSSH;
+                                       break;
+                               case kSecFormatSSH:
+                                       theForm = kSecFormatBSAFE;
+                                       break;
+                               case kSecFormatBSAFE:
+                                       theForm = kSecFormatSSHv2;
+                                       break;
+                               default:
+                                       assert(0);
+                                       ourRtn = false;
+                                       goto done;
+                       }
+               }               /* for each format */
+
+               /* next alg or break if we're done */
+               if(theAlg == maxAlg) {
+                       break;
+               }
+               else switch(theAlg) {
+                       case CSSM_ALGID_RSA:
+                               theAlg = CSSM_ALGID_DSA;
+                               break;
+                       case CSSM_ALGID_DSA:
+                               theAlg = CSSM_ALGID_DH;
+                               break;
+                       case CSSM_ALGID_DH:
+                               theAlg = CSSM_ALGID_ECDSA;
+                               break;
+                       default:
+                               /* i.e. CSSM_ALGID_ECDSA, we already broke at theAlg == maxAlg */
+                               assert(0);
+                               ourRtn = false;
+                               goto done;
+               }
+       }                       /* for each alg */
+done:
+       cuCspDetachUnload(cspHand, CSSM_TRUE);
+       return ourRtn;
+}
+
+/*
+ * Guess an incoming blob's type, format and (for keys only) algorithm
+ * by examining its contents. Returns true on success, in which case
+ * *inputFormat, *itemType, and *keyAlg are all valid. Caller optionally
+ * passes in valid values any number of these as a clue.
+ */
+bool impExpImportGuessByExamination(
+       CFDataRef                       inData,
+       SecExternalFormat   *inputFormat,       // may be kSecFormatUnknown on entry
+       SecExternalItemType     *itemType,              // may be kSecItemTypeUnknown on entry
+       CSSM_ALGORITHMS         *keyAlg)                // CSSM_ALGID_NONE - unknown
+{
+       if( ( (*inputFormat == kSecFormatUnknown) ||
+             (*inputFormat == kSecFormatX509Cert)
+               ) &&
+          ( (*itemType == kSecItemTypeUnknown) ||
+                (*itemType == kSecItemTypeCertificate) ) ) {
+               /*
+                * See if it parses as a cert
+                */
+               CSSM_CL_HANDLE clHand = cuClStartup();
+               if(clHand == 0) {
+                       return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
+               }
+               CSSM_HANDLE cacheHand;
+               CSSM_RETURN crtn;
+               CSSM_DATA cdata = { CFDataGetLength(inData),
+                                                   (uint8 *)CFDataGetBytePtr(inData) };
+               crtn = CSSM_CL_CertCache(clHand, &cdata, &cacheHand);
+               bool brtn = false;
+               if(crtn == CSSM_OK) {
+                       *inputFormat = kSecFormatX509Cert;
+                       *itemType = kSecItemTypeCertificate;
+                       SecImpInferDbg("Inferred kSecFormatX509Cert via CL");
+                       CSSM_CL_CertAbortCache(clHand, cacheHand);
+                       brtn = true;
+               }
+               cuClDetachUnload(clHand);
+               if(brtn) {
+                       return true;
+               }
+       }
+       /* TBD: need way to inquire of P12 lib if this is a valid-looking PFX */
+
+       if( ( (*inputFormat == kSecFormatUnknown) ||
+             (*inputFormat == kSecFormatNetscapeCertSequence)
+               ) &&
+          ( (*itemType == kSecItemTypeUnknown) ||
+                (*itemType == kSecItemTypeAggregate) ) ) {
+               /* See if it's a netscape cert sequence */
+               CSSM_RETURN crtn = impExpNetscapeCertImport(inData, 0, NULL, NULL, NULL);
+               if(crtn == CSSM_OK) {
+                       *inputFormat = kSecFormatNetscapeCertSequence;
+                       *itemType = kSecItemTypeAggregate;
+                       SecImpInferDbg("Inferred netscape-cert-sequence by decoding");
+                       return true;
+               }
+       }
+
+       /* See if it's a key */
+       return impExpGuessKeyParams(inData, inputFormat, itemType, keyAlg);
+}
+
+#pragma mark --- Key Import support ---
+
+/*
+ * Given a context specified via a CSSM_CC_HANDLE, add a new
+ * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
+ * AttributeLength, and an untyped pointer.
+ */
+CSSM_RETURN impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle,
+       uint32 AttributeType,
+       uint32 AttributeLength,
+       const void *AttributePtr)
+{
+       CSSM_CONTEXT_ATTRIBUTE          newAttr;
+
+       newAttr.AttributeType     = AttributeType;
+       newAttr.AttributeLength   = AttributeLength;
+       newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
+       return CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
+}
+
+/*
+ * Free memory via specified plugin's app-level allocator
+ */
+void impExpFreeCssmMemory(
+       CSSM_HANDLE             hand,
+       void                    *p)
+{
+       CSSM_API_MEMORY_FUNCS memFuncs;
+       CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
+       if(crtn) {
+               return;
+       }
+       memFuncs.free_func(p, memFuncs.AllocRef);
+}
+
+/*
+ * Calculate digest of any CSSM_KEY. Unlike older implementations
+ * of this logic, you can actually calculate the public key hash
+ * on any class of key, any format, raw CSP or CSPDL (though if
+ * you're using the CSPDL, the key has to be a reference key
+ * in that CSPDL session).
+ *
+ * Caller must free keyDigest->Data using impExpFreeCssmMemory() since
+ * this is allocated by the CSP's app-specified allocator.
+ */
+CSSM_RETURN impExpKeyDigest(
+       CSSM_CSP_HANDLE cspHand,
+       CSSM_KEY_PTR    key,
+       CSSM_DATA_PTR   keyDigest)              // contents allocd and RETURNED
+{
+       CSSM_DATA_PTR   localDigest;
+       CSSM_CC_HANDLE  ccHand;
+
+       CSSM_RETURN crtn = CSSM_CSP_CreatePassThroughContext(cspHand,
+               key,
+               &ccHand);
+       if(crtn) {
+               return crtn;
+       }
+       crtn = CSSM_CSP_PassThrough(ccHand,
+               CSSM_APPLECSP_KEYDIGEST,
+               NULL,
+               (void **)&localDigest);
+       if(crtn) {
+               SecImpExpDbg("CSSM_CSP_PassThrough(KEY_DIGEST) failure");
+       }
+       else {
+               /*
+                * Give caller the Data referent and we'll free the
+                * CSSM_DATA struct itswelf.
+                */
+               *keyDigest = *localDigest;
+               impExpFreeCssmMemory(cspHand, localDigest);
+       }
+       CSSM_DeleteContext(ccHand);
+       return crtn;
+}
+
+
+/*
+ * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef,
+ * return a refcounted CFStringRef suitable for use with the PKCS12 library.
+ * PKCS12 passphrases in CFData format must be UTF8 encoded.
+ */
+OSStatus impExpPassphraseToCFString(
+       CFTypeRef   passin,
+       CFStringRef *passout)   // may be the same as passin, but refcounted
+{
+       if(CFGetTypeID(passin) == CFStringGetTypeID()) {
+               CFStringRef passInStr = (CFStringRef)passin;
+               CFRetain(passInStr);
+               *passout = passInStr;
+               return errSecSuccess;
+       }
+       else if(CFGetTypeID(passin) == CFDataGetTypeID()) {
+               CFDataRef cfData = (CFDataRef)passin;
+               CFIndex len = CFDataGetLength(cfData);
+               CFStringRef cfStr = CFStringCreateWithBytes(NULL,
+                       CFDataGetBytePtr(cfData), len, kCFStringEncodingUTF8, true);
+               if(cfStr == NULL) {
+                       SecImpExpDbg("Passphrase not in UTF8 format");
+                       return errSecParam;
+               }
+               *passout = cfStr;
+               return errSecSuccess;
+       }
+       else {
+               SecImpExpDbg("Passphrase not CFData or CFString");
+               return errSecParam;
+       }
+}
+
+/*
+ * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef,
+ * return a refcounted CFDataRef whose bytes are suitable for use with
+ * PKCS5 (v1.5 and v2.0) key derivation.
+ */
+OSStatus impExpPassphraseToCFData(
+       CFTypeRef   passin,
+       CFDataRef   *passout)   // may be the same as passin, but refcounted
+{
+       if(CFGetTypeID(passin) == CFDataGetTypeID()) {
+               CFDataRef passInData = (CFDataRef)passin;
+               CFRetain(passInData);
+               *passout = passInData;
+               return errSecSuccess;
+       }
+       else if(CFGetTypeID(passin) == CFStringGetTypeID()) {
+               CFStringRef passInStr = (CFStringRef)passin;
+               CFDataRef outData;
+               outData = CFStringCreateExternalRepresentation(NULL,
+                       passInStr,
+                       kCFStringEncodingUTF8,
+                       0);             // lossByte 0 ==> no loss allowed
+               if(outData == NULL) {
+                       /* Well try with lossy conversion */
+                       SecImpExpDbg("Trying lossy conversion of CFString passphrase to UTF8");
+                       outData = CFStringCreateExternalRepresentation(NULL,
+                               passInStr,
+                               kCFStringEncodingUTF8,
+                               1);
+                       if(outData == NULL) {
+                               SecImpExpDbg("Failure on conversion of CFString passphrase to UTF8");
+                               /* what do we do now, Batman? */
+                               return errSecParam;
+                       }
+               }
+               *passout = outData;
+               return errSecSuccess;
+       }
+       else {
+               SecImpExpDbg("Passphrase not CFData or CFString");
+               return errSecParam;
+       }
+}
+
+/*
+* Add a CFString to a crypto context handle.
+*/
+static CSSM_RETURN impExpAddStringAttr(
+       CSSM_CC_HANDLE ccHand,
+       CFStringRef str,
+       CSSM_ATTRIBUTE_TYPE attrType)
+{
+       /* CFStrings are passed as external rep in UTF8 encoding by convention */
+       CFDataRef outData;
+       outData = CFStringCreateExternalRepresentation(NULL,
+               str, kCFStringEncodingUTF8,     0);             // lossByte 0 ==> no loss allowed
+       if(outData == NULL) {
+               SecImpExpDbg("impExpAddStringAttr: bad string format");
+               return errSecParam;
+       }
+
+       CSSM_DATA attrData;
+       attrData.Data = (uint8 *)CFDataGetBytePtr(outData);
+       attrData.Length = CFDataGetLength(outData);
+       CSSM_RETURN crtn = impExpAddContextAttribute(ccHand, attrType, sizeof(CSSM_DATA),
+               &attrData);
+       CFRelease(outData);
+       if(crtn) {
+               SecImpExpDbg("impExpAddStringAttr: CSSM_UpdateContextAttributes error");
+       }
+       return crtn;
+}
+
+/*
+ * Generate a secure passphrase key. Caller must eventually CSSM_FreeKey the result.
+ */
+static CSSM_RETURN impExpCreatePassKey(
+       const SecKeyImportExportParameters *keyParams,  // required
+       CSSM_CSP_HANDLE         cspHand,                // MUST be CSPDL
+       impExpVerifyPhrase      verifyPhrase,   // for secure passphrase
+       CSSM_KEY_PTR            *passKey)               // mallocd and RETURNED
+{
+       CSSM_RETURN crtn;
+       CSSM_CC_HANDLE ccHand;
+       uint32 verifyAttr;
+       CSSM_DATA dummyLabel;
+       CSSM_KEY_PTR ourKey = NULL;
+
+       SecImpExpDbg("Generating secure passphrase key");
+       ourKey = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY));
+       if(ourKey == NULL) {
+               return errSecAllocate;
+       }
+       memset(ourKey, 0, sizeof(CSSM_KEY));
+
+       crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
+               CSSM_ALGID_SECURE_PASSPHRASE,
+               4,                              // keySizeInBits must be non zero
+               NULL,                   // Seed
+               NULL,                   // Salt
+               NULL,                   // StartDate
+               NULL,                   // EndDate
+               NULL,                   // Params
+               &ccHand);
+       if(crtn) {
+               SecImpExpDbg("impExpCreatePassKey: CSSM_CSP_CreateKeyGenContext error");
+               return crtn;
+       }
+       /* subsequent errors to errOut: */
+
+       /* additional context attributes specific to this type of key gen */
+       assert(keyParams != NULL);                      // or we wouldn't be here
+       if(keyParams->alertTitle != NULL) {
+               crtn = impExpAddStringAttr(ccHand, keyParams->alertTitle,
+                       CSSM_ATTRIBUTE_ALERT_TITLE);
+               if(crtn) {
+                       goto errOut;
+               }
+       }
+       if(keyParams->alertPrompt != NULL) {
+               crtn = impExpAddStringAttr(ccHand, keyParams->alertPrompt,
+                       CSSM_ATTRIBUTE_PROMPT);
+               if(crtn) {
+                       goto errOut;
+               }
+       }
+       verifyAttr = (verifyPhrase == VP_Export) ? 1 : 0;
+       crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_VERIFY_PASSPHRASE,
+               sizeof(uint32), (const void *)((size_t) verifyAttr));
+       if(crtn) {
+               SecImpExpDbg("impExpCreatePassKey: CSSM_UpdateContextAttributes error");
+               goto errOut;
+       }
+
+       dummyLabel.Data = (uint8 *)"Secure Passphrase";
+       dummyLabel.Length = strlen((char *)dummyLabel.Data);
+
+       crtn = CSSM_GenerateKey(ccHand,
+               CSSM_KEYUSE_ANY,
+               CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE,
+               &dummyLabel,
+               NULL,                   // ACL
+               ourKey);
+       if(crtn) {
+               SecImpExpDbg("impExpCreatePassKey: CSSM_GenerateKey error");
+       }
+errOut:
+       CSSM_DeleteContext(ccHand);
+       if(crtn == CSSM_OK) {
+               *passKey = ourKey;
+       }
+       else if(ourKey != NULL) {
+               free(ourKey);
+       }
+       return crtn;
+}
+
+/*
+ * Obtain passphrase, given a SecKeyImportExportParameters.
+ *
+ * Passphrase comes from one of two places: app-specified, in
+ * SecKeyImportExportParameters.passphrase (either as CFStringRef
+ * or CFDataRef); or via the secure passphrase mechanism.
+ *
+ * Passphrase is returned in AT MOST one of two forms:
+ *
+ * -- Secure passphrase is returned as a CSSM_KEY_PTR, which the
+ *    caller must CSSM_FreeKey later as well as free()ing the actual
+ *    CSSM_KEY_PTR.
+ * -- CFTypeRef for app-supplied passphrases. This can be one of
+ *    two types:
+ *
+ *    -- CFStringRef, for use with P12
+ *    -- CFDataRef, for more general use (e.g. for PKCS5).
+ *
+ *    In either case the caller must CFRelease the result.
+ */
+OSStatus impExpPassphraseCommon(
+       const SecKeyImportExportParameters *keyParams,
+       CSSM_CSP_HANDLE                 cspHand,                // MUST be CSPDL, for passKey generation
+       impExpPassphraseForm    phraseForm,
+       impExpVerifyPhrase              verifyPhrase,   // for secure passphrase
+       CFTypeRef                               *phrase,                // RETURNED, or
+       CSSM_KEY_PTR                    *passKey)               // mallocd and RETURNED
+{
+       assert(keyParams != NULL);
+
+       /* Give precedence to secure passphrase */
+       if(keyParams->flags & kSecKeySecurePassphrase) {
+               assert(passKey != NULL);
+               return impExpCreatePassKey(keyParams, cspHand, verifyPhrase, passKey);
+       }
+       else if(keyParams->passphrase != NULL) {
+               CFTypeRef phraseOut;
+               OSStatus ortn;
+               assert(phrase != NULL);
+               switch(phraseForm) {
+                       case SPF_String:
+                               ortn = impExpPassphraseToCFString(keyParams->passphrase,
+                                       (CFStringRef *)&phraseOut);
+                               break;
+                       case SPF_Data:
+                               ortn = impExpPassphraseToCFData(keyParams->passphrase,
+                                       (CFDataRef *)&phraseOut);
+                               break;
+                       default:
+                               /* only called internally */
+                               assert(0);
+                               ortn = errSecParam;
+               }
+               if(ortn == errSecSuccess) {
+                       *phrase = phraseOut;
+               }
+               return ortn;
+       }
+       else {
+               return errSecPassphraseRequired;
+       }
+}
+
+static void ToggleKeyAttribute(
+       CFArrayRef keyAttrs,
+       CFTypeRef attr,
+       CSSM_KEYATTR_FLAGS mask,
+       CSSM_KEYATTR_FLAGS &result)
+{
+       // If the keyAttrs array contains the given attribute,
+       // set the corresponding keyattr flags, otherwise clear them.
+       // (Note: caller verifies that keyAttrs is not NULL.)
+       CFIndex numItems = CFArrayGetCount(keyAttrs);
+       result &= ~(mask);
+       if (numItems > 0) {
+               CFRange range = CFRangeMake(0, numItems);
+               if (CFArrayContainsValue(keyAttrs, range, attr))
+                       result |= mask;
+       }
+}
+
+CSSM_KEYATTR_FLAGS ConvertArrayToKeyAttributes(SecKeyRef aKey, CFArrayRef keyAttrs)
+{
+       CSSM_KEYATTR_FLAGS result = CSSM_KEYATTR_RETURN_DEFAULT;
+
+       if (aKey) {
+               const CSSM_KEY* cssmKey = NULL;
+               if (errSecSuccess == SecKeyGetCSSMKey(aKey, &cssmKey))
+                       result = cssmKey->KeyHeader.KeyAttr;
+       }
+
+       if (!keyAttrs)
+               return result;
+
+       CFMutableArrayRef attrs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+       CFIndex idx, count = CFArrayGetCount(keyAttrs);
+       for (idx=0; idx<count; idx++) {
+               CFTypeRef attr = (CFTypeRef) CFArrayGetValueAtIndex(keyAttrs, idx);
+               if (attr && (CFNumberGetTypeID() == CFGetTypeID(attr))) {
+                       // Convert numeric CSSM keyattr values to equivalent attribute constants
+                       uint32 value;
+                       if (CFNumberGetValue((CFNumberRef)attr, kCFNumberSInt32Type, &value)) {
+                               switch (value) {
+                                       case CSSM_KEYATTR_SENSITIVE:
+                                               attr = kSecAttrIsSensitive;
+                                               break;
+                                       case CSSM_KEYATTR_EXTRACTABLE:
+                                               attr = kSecAttrIsExtractable;
+                                               break;
+                                       case CSSM_KEYATTR_PERMANENT:
+                                               attr = kSecAttrIsPermanent;
+                                               break;
+                                       default:
+                                               attr = NULL;
+                                               break;
+                               }
+                       }
+               }
+               if (attr)
+                       CFArrayAppendValue(attrs, attr);
+       }
+
+       // Set key attribute flag in result if present in the array, otherwise clear
+       ToggleKeyAttribute(attrs, kSecAttrIsSensitive, CSSM_KEYATTR_SENSITIVE, result);
+       ToggleKeyAttribute(attrs, kSecAttrIsExtractable, CSSM_KEYATTR_EXTRACTABLE, result);
+       ToggleKeyAttribute(attrs, kSecAttrIsPermanent, CSSM_KEYATTR_PERMANENT, result);
+
+       // if caller specified an attributes array which omitted kSecAttrIsExtractable,
+       // this implies the sensitive attribute; force it on so that at least one bit
+       // is set. If our result is 0, this is indistinguishable from the case where
+       // no key attributes were specified, causing a default bitmask to be used
+       // in subsequent import code.
+
+       if (0==(result & CSSM_KEYATTR_EXTRACTABLE))
+               result |= CSSM_KEYATTR_SENSITIVE;
+
+       CFRelease(attrs);
+       return result;
+}
+
+Boolean ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(SecKeyRef aKey,
+       const SecItemImportExportKeyParameters* newPtr, SecKeyImportExportParameters* oldPtr)
+{
+       Boolean result = false;
+
+       if (NULL != oldPtr && NULL != newPtr)
+       {
+               oldPtr->version = newPtr->version;
+               oldPtr->flags = newPtr->flags;
+               oldPtr->passphrase = newPtr->passphrase;
+               oldPtr->alertTitle = newPtr->alertTitle;
+               oldPtr->alertPrompt = newPtr->alertPrompt;
+               oldPtr->accessRef = newPtr->accessRef;
+               oldPtr->keyUsage = ConvertArrayToKeyUsage(newPtr->keyUsage);
+               oldPtr->keyAttributes = ConvertArrayToKeyAttributes(aKey, newPtr->keyAttributes);
+               result = true;
+       }
+       return result;
+}
+