--- /dev/null
+/*
+ * 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);
+}