]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_pkcs12/lib/pkcs12Utils.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_pkcs12 / lib / pkcs12Utils.cpp
diff --git a/libsecurity_pkcs12/lib/pkcs12Utils.cpp b/libsecurity_pkcs12/lib/pkcs12Utils.cpp
new file mode 100644 (file)
index 0000000..813406e
--- /dev/null
@@ -0,0 +1,833 @@
+/*
+ * Copyright (c) 2003-2004 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@
+ */
+
+/*
+ * pkcs12Utils.cpp
+ */
+
+#include "pkcs12Utils.h"
+#include <string.h>
+#include "pkcs7Templates.h"
+#include "pkcs12Templates.h"
+#include "pkcs12Crypto.h"
+#include "pkcs12Debug.h"
+#include <security_asn1/nssUtils.h>
+#include <Security/secasn1t.h>
+#include <security_utilities/devrandom.h>
+#include <security_utilities/errors.h>
+#include <security_cdsa_utils/cuCdsaUtils.h>
+#include <Security/oidsattr.h>
+#include <Security/oidsalg.h>
+#include <Security/cssmapple.h>
+#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
+
+/* malloc a NULL-ed array of pointers of size num+1 */
+void **p12NssNullArray(
+       uint32 num,
+       SecNssCoder &coder)
+{
+       unsigned len = (num + 1) * sizeof(void *);
+       void **p = (void **)coder.malloc(len);
+       memset(p, 0, len);
+       return p;
+}
+
+/* CSSM_DATA --> uint32. Returns true if OK. */
+bool p12DataToInt(
+       const CSSM_DATA &cdata,
+       uint32 &u)
+{
+       if((cdata.Length == 0) || (cdata.Data == NULL)) {
+               /* default/not present */
+               u = 0;
+               return true;
+       }
+       uint32 len = cdata.Length;
+       if(len > sizeof(uint32)) {
+               return false;
+       }
+       
+       uint32 rtn = 0;
+       uint8 *cp = cdata.Data;
+       for(uint32 i=0; i<len; i++) {
+               rtn = (rtn << 8) | *cp++;
+       }
+       u = rtn;
+       return true;
+}
+
+/* uint32 --> CSSM_DATA */
+void p12IntToData(
+       uint32 num,
+       CSSM_DATA &cdata,
+       SecNssCoder &coder)
+{
+       uint32 len = 0;
+       
+       if(num < 0x100) {
+               len = 1;
+       }
+       else if(num < 0x10000) {
+               len = 2;
+       }
+       else if(num < 0x1000000) {
+               len = 3;
+       }
+       else {
+               len = 4;
+       }
+       coder.allocItem(cdata, len);
+       uint8 *cp = &cdata.Data[len - 1];
+       for(unsigned i=0; i<len; i++) {
+               *cp-- = num & 0xff;
+               num >>= 8;
+       }
+}
+
+/* CFDataRef <--> CSSM_DATA */
+CFDataRef p12CssmDataToCf(
+       const CSSM_DATA &c)
+{
+       return CFDataCreate(NULL, c.Data, c.Length);
+}
+
+void p12CfDataToCssm(
+       CFDataRef cf,
+       CSSM_DATA &c,
+       SecNssCoder &coder)
+{
+       coder.allocCopyItem(CFDataGetBytePtr(cf),
+               CFDataGetLength(cf), c);
+}
+
+/*
+ * Attempt to convert a CFStringRef, which represents a SafeBag's
+ * FriendlyName, to a UTF8-encoded CSSM_DATA. The CSSM_DATA and its
+ * referent are allocated in the specified SecNssCoder's memory.
+ * No guarantee that this conversion works. If it doesn't we return 
+ * NULL and caller must be prepared to deal with that. 
+ */
+CSSM_DATA_PTR p12StringToUtf8(
+       CFStringRef cfStr,
+       SecNssCoder &coder)
+{
+       if(cfStr == NULL) {
+               return NULL;
+       }
+       CFIndex strLen = CFStringGetLength(cfStr);
+       if(strLen == 0) {
+               return NULL;
+       }
+       CSSM_DATA_PTR rtn = coder.mallocn<CSSM_DATA>();
+       coder.allocItem(*rtn, strLen + 1);
+       if(!CFStringGetCString(cfStr, (char *)rtn->Data,strLen + 1,
+                       kCFStringEncodingUTF8)) {
+               /* not convertible from native Unicode to UTF8 */
+               return NULL;
+       }
+       return rtn;
+}
+
+/*
+ * Enum to string mappper.
+ * Maybe DEBUG only.
+ */
+/*
+ * Each type of attribute has a name/value pair in a table of these:
+ */
+typedef struct {
+       unsigned                value;
+       const char              *name;
+} p12NameValuePair;
+
+/* declare one entry in a table of p12NameValuePair */
+#define NVP(attr)              {attr, #attr}
+
+/* the NULL entry which terminates all p12NameValuePair tables */
+#define NVP_END                {0, NULL}
+
+static const p12NameValuePair p7CITypeNames[] = 
+{
+       NVP(CT_None),
+       NVP(CT_Data),
+       NVP(CT_SignedData),
+       NVP(CT_EnvData),
+       NVP(CT_SignedEnvData),
+       NVP(CT_DigestData),
+       NVP(CT_EncryptedData),
+       NVP_END
+};
+
+static const p12NameValuePair p12BagTypeNames[] = 
+{
+       NVP(BT_None),
+       NVP(BT_KeyBag),
+       NVP(BT_ShroudedKeyBag),
+       NVP(BT_CertBag),
+       NVP(BT_CrlBag),
+       NVP(BT_SecretBag),
+       NVP(BT_SafeContentsBag),
+       NVP_END
+};
+
+static const char *typeToStr(
+       unsigned type,
+       const p12NameValuePair *table)
+{
+       while(table->name) {
+               if(table->value == type) {
+                       return table->name;
+               }
+               table++;
+       }
+       return "Unknown";
+}
+
+const char *p12BagTypeStr(
+       NSS_P12_SB_Type type)
+{
+       return typeToStr(type, p12BagTypeNames);
+}
+
+const char *p7ContentInfoTypeStr(
+       NSS_P7_CI_Type type)
+{
+       return typeToStr(type, p7CITypeNames);
+}
+
+/*
+ * OIDS for P12 and PKCS5 v1.5 (PBES1) encrypt and decrypt map to the following
+ * attributes.
+ */
+typedef struct {
+       const CSSM_OID          *oid;
+       CSSM_ALGORITHMS         keyAlg;         // e.g., CSSM_ALGID_DES
+       CSSM_ALGORITHMS         encrAlg;        // e.g., CSSM_ALGID_3DES_3KEY_EDE
+       CSSM_ALGORITHMS         pbeHashAlg;     // SHA1 or MD5
+       uint32                          keySizeInBits;
+       uint32                          blockSizeInBytes;       // for IV, optional
+       CSSM_PADDING            padding;        // CSSM_PADDING_PKCS7, etc.
+       CSSM_ENCRYPT_MODE       mode;           // CSSM_ALGMODE_CBCPadIV8, etc.
+       PKCS_Which                      pkcs;           // PW_PKCS12 (for this module) or PW_PKCS5_v1_5
+} PKCSOidInfo;
+
+static const PKCSOidInfo pkcsOidInfos[] = {
+       /* PKCS12 first, the ones this module uses */
+       { 
+               &CSSMOID_PKCS12_pbeWithSHAAnd128BitRC4,
+               CSSM_ALGID_RC4,
+               CSSM_ALGID_RC4,
+               CSSM_ALGID_SHA1,
+               128,
+               0,                                      // RC4 is a stream cipher
+               CSSM_PADDING_NONE,
+               CSSM_ALGMODE_NONE,
+               PW_PKCS12
+       },
+       { 
+               &CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4,
+               CSSM_ALGID_RC4,
+               CSSM_ALGID_RC4,
+               CSSM_ALGID_SHA1,
+               40,
+               0,                                      // RC4 is a stream cipher
+               CSSM_PADDING_NONE,
+               CSSM_ALGMODE_NONE,
+               PW_PKCS12
+       },
+       { 
+               &CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC,
+               CSSM_ALGID_3DES_3KEY,
+               CSSM_ALGID_3DES_3KEY_EDE,
+               CSSM_ALGID_SHA1,
+               64 * 3,
+               8,      
+               CSSM_PADDING_PKCS7,
+               CSSM_ALGMODE_CBCPadIV8,
+               PW_PKCS12
+       },
+       { 
+               &CSSMOID_PKCS12_pbeWithSHAAnd2Key3DESCBC,
+               CSSM_ALGID_3DES_2KEY,
+               CSSM_ALGID_3DES_2KEY_EDE,
+               CSSM_ALGID_SHA1,
+               64 * 2,
+               8,      
+               CSSM_PADDING_PKCS7,
+               CSSM_ALGMODE_CBCPadIV8,
+               PW_PKCS12
+       },
+       { 
+               &CSSMOID_PKCS12_pbeWithSHAAnd128BitRC2CBC,
+               CSSM_ALGID_RC2,
+               CSSM_ALGID_RC2,
+               CSSM_ALGID_SHA1,
+               128,
+               8,      
+               CSSM_PADDING_PKCS7,
+               CSSM_ALGMODE_CBCPadIV8,
+               PW_PKCS12
+       },
+       { 
+               &CSSMOID_PKCS12_pbewithSHAAnd40BitRC2CBC,
+               CSSM_ALGID_RC2,
+               CSSM_ALGID_RC2,
+               CSSM_ALGID_SHA1,
+               40,
+               8,      
+               CSSM_PADDING_PKCS7,
+               CSSM_ALGMODE_CBCPadIV8,
+               PW_PKCS12
+       },
+       
+       /* PKCS5 v1.5, used for SecImportExport module */
+       {
+               &CSSMOID_PKCS5_pbeWithMD2AndDES,
+               CSSM_ALGID_DES,
+               CSSM_ALGID_DES,
+               CSSM_ALGID_MD2,
+               64,
+               8,
+               CSSM_PADDING_PKCS7,
+               CSSM_ALGMODE_CBCPadIV8,
+               PW_PKCS5_v1_5
+       },
+       {
+               &CSSMOID_PKCS5_pbeWithMD2AndRC2,
+               CSSM_ALGID_RC2,
+               CSSM_ALGID_RC2,
+               CSSM_ALGID_MD2,
+               64,
+               8,
+               CSSM_PADDING_PKCS7,
+               CSSM_ALGMODE_CBCPadIV8,
+               PW_PKCS5_v1_5
+       },
+       {
+               &CSSMOID_PKCS5_pbeWithMD5AndDES,
+               CSSM_ALGID_DES,
+               CSSM_ALGID_DES,
+               CSSM_ALGID_MD5,
+               64,
+               8,
+               CSSM_PADDING_PKCS7,
+               CSSM_ALGMODE_CBCPadIV8,
+               PW_PKCS5_v1_5
+       },
+       {
+               &CSSMOID_PKCS5_pbeWithMD5AndRC2,
+               CSSM_ALGID_RC2,
+               CSSM_ALGID_RC2,
+               CSSM_ALGID_MD5,
+               64,
+               8,
+               CSSM_PADDING_PKCS7,
+               CSSM_ALGMODE_CBCPadIV8,
+               PW_PKCS5_v1_5
+       },
+       {
+               &CSSMOID_PKCS5_pbeWithSHA1AndDES,
+               CSSM_ALGID_DES,
+               CSSM_ALGID_DES,
+               CSSM_ALGID_SHA1,
+               64,
+               8,
+               CSSM_PADDING_PKCS7,
+               CSSM_ALGMODE_CBCPadIV8,
+               PW_PKCS5_v1_5
+       },
+       {
+               &CSSMOID_PKCS5_pbeWithSHA1AndRC2,
+               CSSM_ALGID_RC2,
+               CSSM_ALGID_RC2,
+               CSSM_ALGID_SHA1,
+               64,
+               8,
+               CSSM_PADDING_PKCS7,
+               CSSM_ALGMODE_CBCPadIV8,
+               PW_PKCS5_v1_5
+       },
+       
+       /* finally one for PKCS5 v2.0, which has its own means of 
+        * cooking up all the parameters */
+       {
+               &CSSMOID_PKCS5_PBES2,
+               CSSM_ALGID_NONE,
+               CSSM_ALGID_NONE,
+               CSSM_ALGID_NONE,
+               0, 0, 0, 0, 
+               PW_PKCS5_v2
+       }
+};
+
+#define NUM_PKCS_OID_INFOS (sizeof(pkcsOidInfos) / sizeof(pkcsOidInfos[1]))
+
+/* map an OID to the components */
+/* returns false if OID not found */
+
+/* 
+ * NOTE: as of March 8 2004 this is also used by the SecImportExport
+ * module...not just PKCS12!
+ */
+bool pkcsOidToParams(
+       const CSSM_OID          *oid,
+       CSSM_ALGORITHMS         &keyAlg,                // e.g., CSSM_ALGID_DES
+       CSSM_ALGORITHMS         &encrAlg,               // e.g., CSSM_ALGID_3DES_3KEY_EDE
+       CSSM_ALGORITHMS         &pbeHashAlg,    // SHA1 or MD5
+       uint32                          &keySizeInBits,
+       uint32                          &blockSizeInBytes,      // for IV, optional
+       CSSM_PADDING            &padding,               // CSSM_PADDING_PKCS7, etc.
+       CSSM_ENCRYPT_MODE       &mode,                  // CSSM_ALGMODE_CBCPadIV8, etc.
+       PKCS_Which                      &pkcs)                  // PW_PKCS5_v1_5 or PW_PKCS12
+{
+       const PKCSOidInfo *info = pkcsOidInfos;
+       pkcs = PW_None;
+       
+       for(unsigned dex=0; dex<NUM_PKCS_OID_INFOS; dex++) {
+               if(nssCompareCssmData(oid, info->oid)) {
+                       keyAlg                   = info->keyAlg;
+                       encrAlg                  = info->encrAlg;
+                       pbeHashAlg               = info->pbeHashAlg;
+                       keySizeInBits    = info->keySizeInBits;
+                       blockSizeInBytes = info->blockSizeInBytes;
+                       padding                  = info->padding;
+                       mode                     = info->mode;
+                       pkcs                     = info->pkcs;
+                       return true;
+               }
+               info++;
+       }
+       return false;
+}
+
+/*
+ * Verify MAC on an existing PFX.  
+ */
+CSSM_RETURN p12VerifyMac(
+       const NSS_P12_DecodedPFX        &pfx,
+       CSSM_CSP_HANDLE                         cspHand,
+       const CSSM_DATA                         *pwd,   // unicode, double null terminated
+       const CSSM_KEY                          *passKey,
+       SecNssCoder                                     &coder) // for temp mallocs
+{
+       if(pfx.macData == NULL) {
+               return CSSMERR_CSP_INVALID_SIGNATURE;
+       }
+       NSS_P12_MacData &macData = *pfx.macData;
+       NSS_P7_DigestInfo &digestInfo  = macData.mac;
+       CSSM_OID &algOid = digestInfo.digestAlgorithm.algorithm;
+       CSSM_ALGORITHMS macAlg;
+       if(!cssmOidToAlg(&algOid, &macAlg)) {
+               return CSSMERR_CSP_INVALID_ALGORITHM;
+       }
+       uint32 iterCount = 0;
+       CSSM_DATA &citer = macData.iterations;
+       if(!p12DataToInt(citer, iterCount)) {
+               return CSSMERR_CSP_INVALID_ATTR_ROUNDS;
+       }
+       if(iterCount == 0) {
+               /* optional, default 1 */
+               iterCount = 1;
+       }
+
+       /*
+        * In classic fashion, the PKCS12 spec now says:
+        *
+        *      When password integrity mode is used to secure a PFX PDU, 
+        *      an SHA-1 HMAC is computed on the BER-encoding of the contents 
+        *      of the content field of the authSafe field in the PFX PDU.
+        *
+        * So here we go.
+        */
+       CSSM_DATA genMac;
+       CSSM_RETURN crtn = p12GenMac(cspHand, *pfx.authSafe.content.data, 
+               macAlg, iterCount, macData.macSalt, pwd, passKey, coder, genMac);
+       if(crtn) {
+               return crtn;
+       }
+       if(nssCompareCssmData(&genMac, &digestInfo.digest)) {
+               return CSSM_OK;
+       }
+       else {
+               return CSSMERR_CSP_VERIFY_FAILED;
+       }
+}
+
+/* we generate 8 random bytes of salt */
+#define P12_SALT_LEN           8
+
+void p12GenSalt(
+       CSSM_DATA &salt,
+       SecNssCoder &coder)
+{
+       DevRandomGenerator rng;
+       coder.allocItem(salt, P12_SALT_LEN);
+       rng.random(salt.Data, P12_SALT_LEN);
+}
+
+/* 
+ * Generate random label string to allow associating an imported private
+ * key with a cert.
+ */
+void p12GenLabel(
+       CSSM_DATA &label,
+       SecNssCoder &coder)
+{
+       /* first a random uint32 */
+       uint8 d[4];
+       DevRandomGenerator rng;
+       rng.random(d, 4);
+       CSSM_DATA cd = {4, d};
+       uint32 i;
+       p12DataToInt(cd, i);
+       
+       /* sprintf that into a real string */
+       coder.allocItem(label, 9);
+       memset(label.Data, 0, 9);
+       sprintf((char *)label.Data, "%08X", (unsigned)i);
+}
+
+/* NULL algorithm parameters */
+
+static const uint8 nullAlg[2] = {SEC_ASN1_NULL, 0};
+
+void p12NullAlgParams(
+       CSSM_X509_ALGORITHM_IDENTIFIER &algId)
+{
+       CSSM_DATA &p = algId.parameters;
+       p.Data = (uint8 *)nullAlg;
+       p.Length = 2;
+}
+
+/*
+ * Free memory via specified plugin's app-level allocator
+ */
+void freeCssmMemory(
+       CSSM_HANDLE     hand,
+       void                    *p)
+{
+       CSSM_API_MEMORY_FUNCS memFuncs;
+       CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
+       if(crtn) {
+               p12LogCssmError("CSSM_GetAPIMemoryFunctions", crtn);
+               /* oh well, leak and continue */
+               return;
+       }
+       memFuncs.free_func(p, memFuncs.AllocRef);
+}
+
+/*
+ * Find private key by label, modify its Label attr to be the
+ * hash of the associated public key. 
+ * Also optionally re-sets the key's PrintName attribute; used to reset
+ * this attr from the random label we create when first unwrap it 
+ * to the friendly name we find later after parsing attributes.
+ * Detection of a duplicate key when updating the key's attributes
+ * results in a lookup of the original key and returning it in
+ * foundKey.
+ */
+CSSM_RETURN p12SetPubKeyHash(
+       CSSM_CSP_HANDLE         cspHand,                // where the key lives
+       CSSM_DL_DB_HANDLE       dlDbHand,               // ditto
+       CSSM_DATA                       &keyLabel,              // for DB lookup
+       CSSM_DATA_PTR           newPrintName,   // optional
+       SecNssCoder                     &coder,                 // for mallocing newLabel
+       CSSM_DATA                       &newLabel,              // RETURNED with label as hash
+       CSSM_KEY_PTR            &foundKey)              // RETURNED
+{
+       CSSM_QUERY                                              query;
+       CSSM_SELECTION_PREDICATE                predicate;
+       CSSM_DB_UNIQUE_RECORD_PTR               record = NULL;
+       CSSM_RETURN                                             crtn;
+       CSSM_HANDLE                                             resultHand = 0;
+       CSSM_DATA                                               keyData = {0, NULL};
+       CSSM_CC_HANDLE                                  ccHand = 0;
+       CSSM_KEY_PTR                                    privKey = NULL;
+       CSSM_DATA_PTR                                   keyDigest = NULL;
+       
+       assert(cspHand != 0);
+       query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
+       query.Conjunctive = CSSM_DB_NONE;
+       query.NumSelectionPredicates = 1;
+       predicate.DbOperator = CSSM_DB_EQUAL;
+       
+       predicate.Attribute.Info.AttributeNameFormat = 
+               CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
+       predicate.Attribute.Info.Label.AttributeName = 
+               (char*) P12_KEY_ATTR_LABEL_AND_HASH;
+       predicate.Attribute.Info.AttributeFormat = 
+               CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
+       /* hope this cast is OK */
+       predicate.Attribute.Value = &keyLabel;
+       query.SelectionPredicate = &predicate;
+       
+       query.QueryLimits.TimeLimit = 0;        // FIXME - meaningful?
+       query.QueryLimits.SizeLimit = 1;        // FIXME - meaningful?
+       query.QueryFlags = CSSM_QUERY_RETURN_DATA;      
+
+       /* build Record attribute with one or two attrs */
+       CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
+       CSSM_DB_ATTRIBUTE_DATA attr[2];
+       attr[0].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
+       attr[0].Info.Label.AttributeName = (char*) P12_KEY_ATTR_LABEL_AND_HASH;
+       attr[0].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
+       if(newPrintName) {
+               attr[1].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
+               attr[1].Info.Label.AttributeName = (char*) P12_KEY_ATTR_PRINT_NAME;
+               attr[1].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
+       }
+       recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
+       recordAttrs.NumberOfAttributes = newPrintName ? 2 : 1;
+       recordAttrs.AttributeData = attr;
+       
+       crtn = CSSM_DL_DataGetFirst(dlDbHand,
+               &query,
+               &resultHand,
+               &recordAttrs,
+               &keyData,                       // theData
+               &record);
+       /* abort only on success */
+       if(crtn != CSSM_OK) {
+               p12LogCssmError("CSSM_DL_DataGetFirst", crtn);
+               p12ErrorLog("***p12SetPubKeyHash: can't find private key\n");
+               return crtn;
+       }
+       /* subsequent errors to errOut: */
+       if(keyData.Data == NULL) {
+               p12ErrorLog("***p12SetPubKeyHash: private key lookup failure\n");
+               crtn = CSSMERR_CSSM_INTERNAL_ERROR;
+               goto errOut;
+       }
+       privKey = (CSSM_KEY_PTR)keyData.Data;
+       
+       /* public key hash via passthrough - works on any key, any CSP/CSPDL.... */
+       /*
+        * Warning! This relies on the current default ACL meaning "allow this
+        * current app to access this private key" since we created the key. 
+        */
+       crtn = CSSM_CSP_CreatePassThroughContext(cspHand, privKey, &ccHand);
+       if(crtn) {
+               p12LogCssmError("CSSM_CSP_CreatePassThroughContext", crtn);
+               goto errOut;
+       }
+       crtn = CSSM_CSP_PassThrough(ccHand,
+               CSSM_APPLECSP_KEYDIGEST,
+               NULL,
+               (void **)&keyDigest);
+       if(crtn) {
+               p12LogCssmError("CSSM_CSP_PassThrough", crtn);
+               goto errOut;
+       }
+
+       /* 
+        * Replace Label attr data with hash.
+        * NOTE: the module which allocated this attribute data - a DL -
+        * was loaded and attached by out client layer, not by us. Thus 
+        * we can't use the memory allocator functions *we* used when 
+        * attaching to the CSP - we have to use the ones
+        * which the client registered with the DL.
+        */
+       freeCssmMemory(dlDbHand.DLHandle, attr[0].Value->Data);
+       freeCssmMemory(dlDbHand.DLHandle, attr[0].Value);
+       if(newPrintName) {
+               freeCssmMemory(dlDbHand.DLHandle, attr[1].Value->Data);
+               freeCssmMemory(dlDbHand.DLHandle, attr[1].Value);
+       }
+       /* modify key attributes */
+       attr[0].Value = keyDigest;
+       if(newPrintName) {
+               attr[1].Value = newPrintName;
+       }
+       crtn = CSSM_DL_DataModify(dlDbHand,
+                       CSSM_DL_DB_RECORD_PRIVATE_KEY,
+                       record,
+                       &recordAttrs,
+            NULL,                              // DataToBeModified
+                       CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
+       switch(crtn) {
+               case CSSM_OK:
+                       /* give caller the key's new label */
+                       coder.allocCopyItem(*keyDigest, newLabel);
+                       break;
+               default:
+                       p12LogCssmError("CSSM_DL_DataModify", crtn);
+                       break;
+               case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA:
+               {
+                       /* 
+                        * Special case: dup private key. The label we just tried to modify is 
+                        * the public key hash so we can be confident that this really is a dup. 
+                        * Delete it, look up the original, and return the original to caller. 
+                        */ 
+                       CSSM_RETURN drtn = CSSM_DL_DataDelete(dlDbHand, record);
+                       if(drtn) {
+                               p12LogCssmError("CSSM_DL_DataDelete on dup key", drtn);
+                               crtn = drtn;
+                               break;
+                       }
+
+                       /* Free items created in last search */
+                       CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
+                       resultHand = 0;
+                       CSSM_DL_FreeUniqueRecord(dlDbHand, record);
+                       record = NULL;
+                       
+                       /* lookup by label as public key hash this time */
+                       predicate.Attribute.Value = keyDigest;
+                       drtn = CSSM_DL_DataGetFirst(dlDbHand,
+                               &query,
+                               &resultHand,
+                               NULL,                           // no attrs this time
+                               &keyData,               
+                               &record);
+                       if(drtn) {
+                               p12LogCssmError("CSSM_DL_DataGetFirst on original key", crtn);
+                               crtn = drtn;
+                               break;
+                       }
+                       foundKey = (CSSM_KEY_PTR)keyData.Data;
+                       /* give caller the key's actual label */
+                       coder.allocCopyItem(*keyDigest, newLabel);
+                       break;
+               }
+       }
+       
+errOut:
+       /* free resources */
+       if(resultHand) {
+               CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
+       }
+       if(record) {
+               CSSM_DL_FreeUniqueRecord(dlDbHand, record);
+       }
+       if(ccHand) {
+               CSSM_DeleteContext(ccHand);
+       }
+       if(privKey) {
+               /* key created by the CSPDL */
+               CSSM_FreeKey(cspHand, NULL, privKey, CSSM_FALSE);
+               freeCssmMemory(dlDbHand.DLHandle, privKey);
+       }
+       if(keyDigest)  {
+               /* mallocd by someone else's CSP */
+               freeCssmMemory(cspHand, keyDigest->Data);
+               freeCssmMemory(cspHand, keyDigest);
+       }
+       return crtn;
+}
+
+/*
+ * 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 p12AddContextAttribute(CSSM_CC_HANDLE CCHandle,
+       uint32 AttributeType,
+       uint32 AttributeLength,
+       const void *AttributePtr)
+{
+       CSSM_CONTEXT_ATTRIBUTE          newAttr;        
+       CSSM_RETURN                                     crtn;
+       
+       newAttr.AttributeType     = AttributeType;
+       newAttr.AttributeLength   = AttributeLength;
+       newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
+       crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
+       if(crtn) {
+               p12LogCssmError("CSSM_UpdateContextAttributes", crtn);
+       }
+       return crtn;
+}
+
+/*
+ * Find private key by specified label, delete it.
+ */
+CSSM_RETURN p12DeleteKey(
+       CSSM_DL_DB_HANDLE dlDbHand, 
+       const CSSM_DATA &keyLabel)
+{
+       CSSM_QUERY                                              query;
+       CSSM_SELECTION_PREDICATE                predicate;
+       CSSM_DB_UNIQUE_RECORD_PTR               record = NULL;
+       CSSM_RETURN                                             crtn;
+       CSSM_HANDLE                                             resultHand = 0;
+       
+       query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
+       query.Conjunctive = CSSM_DB_NONE;
+       query.NumSelectionPredicates = 1;
+       predicate.DbOperator = CSSM_DB_EQUAL;
+       
+       predicate.Attribute.Info.AttributeNameFormat = 
+               CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
+       predicate.Attribute.Info.Label.AttributeName = 
+               (char*) P12_KEY_ATTR_LABEL_AND_HASH;
+       predicate.Attribute.Info.AttributeFormat = 
+               CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
+       predicate.Attribute.Value = const_cast<CSSM_DATA_PTR>(&keyLabel);
+       
+       query.SelectionPredicate = &predicate;
+       query.QueryLimits.TimeLimit = 0;
+       query.QueryLimits.SizeLimit = 1;
+       query.QueryFlags = 0;
+
+       crtn = CSSM_DL_DataGetFirst(dlDbHand,
+               &query,
+               &resultHand,
+               NULL,                   // attrs - don't need 'em
+               NULL,                   // theData - don't need it
+               &record);
+       /* abort only on success */
+       if(crtn) {
+               p12LogCssmError("CSSM_DL_DataGetFirst", crtn);
+               p12ErrorLog("***p12DeleteKey: can't find private key\n");
+               return crtn;
+       }
+
+       crtn = CSSM_DL_DataDelete(dlDbHand, record);
+       if(crtn) {
+               p12LogCssmError("CSSM_DL_DataDelete", crtn);
+               p12ErrorLog("***p12DeleteKey: can't delete private key\n");
+       }
+       
+       CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
+       CSSM_DL_FreeUniqueRecord(dlDbHand, record);
+       return crtn;
+}
+
+/* convert App passphrase to array of chars used in P12 PBE */
+void p12ImportPassPhrase(
+       CFStringRef             inPhrase,
+       SecNssCoder             &coder,
+       CSSM_DATA               &outPhrase)
+{
+       CFDataRef cfData = CFStringCreateExternalRepresentation(NULL,
+               inPhrase, kCFStringEncodingUTF8, 0);
+       if(cfData == NULL) {
+               p12ErrorLog("***p12ImportPassPhrase: can't convert passphrase to UTF8\n");
+               MacOSError::throwMe(paramErr);
+       }
+       unsigned keyLen = CFDataGetLength(cfData);
+       coder.allocItem(outPhrase, keyLen);
+       memmove(outPhrase.Data, CFDataGetBytePtr(cfData), keyLen);
+       CFRelease(cfData);
+}