X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_pkcs12/lib/pkcs12Utils.cpp diff --git a/Security/libsecurity_pkcs12/lib/pkcs12Utils.cpp b/Security/libsecurity_pkcs12/lib/pkcs12Utils.cpp new file mode 100644 index 00000000..7ad4593c --- /dev/null +++ b/Security/libsecurity_pkcs12/lib/pkcs12Utils.cpp @@ -0,0 +1,832 @@ +/* + * Copyright (c) 2003-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@ + */ + +/* + * pkcs12Utils.cpp + */ + +#include "pkcs12Utils.h" +#include +#include "pkcs7Templates.h" +#include "pkcs12Templates.h" +#include "pkcs12Crypto.h" +#include "pkcs12Debug.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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; + } + CSSM_SIZE len = cdata.Length; + if(len > sizeof(uint32)) { + return false; + } + + uint32 rtn = 0; + uint8 *cp = cdata.Data; + for(uint32 i=0; i 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>= 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(); + 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; dexoid)) { + 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(&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(errSecParam); + } + CFIndex keyLen = CFDataGetLength(cfData); + coder.allocItem(outPhrase, keyLen); + memmove(outPhrase.Data, CFDataGetBytePtr(cfData), keyLen); + CFRelease(cfData); +}