1/*
2 * Copyright (c) 2003-2004,2011-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * pkcs12Utils.cpp
26 */
27
28#include "pkcs12Utils.h"
29#include <string.h>
30#include "pkcs7Templates.h"
31#include "pkcs12Templates.h"
32#include "pkcs12Crypto.h"
33#include "pkcs12Debug.h"
34#include <security_asn1/nssUtils.h>
35#include <Security/secasn1t.h>
36#include <security_utilities/devrandom.h>
37#include <security_utilities/errors.h>
38#include <security_cdsa_utils/cuCdsaUtils.h>
39#include <Security/oidsattr.h>
40#include <Security/oidsalg.h>
41#include <Security/cssmapple.h>
42
43/* malloc a NULL-ed array of pointers of size num+1 */
44void **p12NssNullArray(
45	uint32 num,
46	SecNssCoder &coder)
47{
48	unsigned len = (num + 1) * sizeof(void *);
49	void **p = (void **)coder.malloc(len);
50	memset(p, 0, len);
51	return p;
52}
53
54/* CSSM_DATA --> uint32. Returns true if OK. */
55bool p12DataToInt(
56	const CSSM_DATA &cdata,
57	uint32 &u)
58{
59	if((cdata.Length == 0) || (cdata.Data == NULL)) {
60		/* default/not present */
61		u = 0;
62		return true;
63	}
64	CSSM_SIZE len = cdata.Length;
65	if(len > sizeof(uint32)) {
66		return false;
67	}
68
69	uint32 rtn = 0;
70	uint8 *cp = cdata.Data;
71	for(uint32 i=0; i<len; i++) {
72		rtn = (rtn << 8) | *cp++;
73	}
74	u = rtn;
75	return true;
76}
77
78/* uint32 --> CSSM_DATA */
79void p12IntToData(
80	uint32 num,
81	CSSM_DATA &cdata,
82	SecNssCoder &coder)
83{
84	uint32 len = 0;
85
86	if(num < 0x100) {
87		len = 1;
88	}
89	else if(num < 0x10000) {
90		len = 2;
91	}
92	else if(num < 0x1000000) {
93		len = 3;
94	}
95	else {
96		len = 4;
97	}
98	coder.allocItem(cdata, len);
99	uint8 *cp = &cdata.Data[len - 1];
100	for(unsigned i=0; i<len; i++) {
101		*cp-- = num & 0xff;
102		num >>= 8;
103	}
104}
105
106/* CFDataRef <--> CSSM_DATA */
107CFDataRef p12CssmDataToCf(
108	const CSSM_DATA &c)
109{
110	return CFDataCreate(NULL, c.Data, c.Length);
111}
112
113void p12CfDataToCssm(
114	CFDataRef cf,
115	CSSM_DATA &c,
116	SecNssCoder &coder)
117{
118	coder.allocCopyItem(CFDataGetBytePtr(cf),
119		CFDataGetLength(cf), c);
120}
121
122/*
123 * Attempt to convert a CFStringRef, which represents a SafeBag's
124 * FriendlyName, to a UTF8-encoded CSSM_DATA. The CSSM_DATA and its
125 * referent are allocated in the specified SecNssCoder's memory.
126 * No guarantee that this conversion works. If it doesn't we return
127 * NULL and caller must be prepared to deal with that.
128 */
129CSSM_DATA_PTR p12StringToUtf8(
130	CFStringRef cfStr,
131	SecNssCoder &coder)
132{
133	if(cfStr == NULL) {
134		return NULL;
135	}
136	CFIndex strLen = CFStringGetLength(cfStr);
137	if(strLen == 0) {
138		return NULL;
139	}
140	CSSM_DATA_PTR rtn = coder.mallocn<CSSM_DATA>();
141	coder.allocItem(*rtn, strLen + 1);
142	if(!CFStringGetCString(cfStr, (char *)rtn->Data,strLen + 1,
143			kCFStringEncodingUTF8)) {
144		/* not convertible from native Unicode to UTF8 */
145		return NULL;
146	}
147	return rtn;
148}
149
150/*
151 * Enum to string mappper.
152 * Maybe DEBUG only.
153 */
154/*
155 * Each type of attribute has a name/value pair in a table of these:
156 */
157typedef struct {
158	unsigned		value;
159	const char 		*name;
160} p12NameValuePair;
161
162/* declare one entry in a table of p12NameValuePair */
163#define NVP(attr)		{attr, #attr}
164
165/* the NULL entry which terminates all p12NameValuePair tables */
166#define NVP_END		{0, NULL}
167
168static const p12NameValuePair p7CITypeNames[] =
169{
170	NVP(CT_None),
171	NVP(CT_Data),
172	NVP(CT_SignedData),
173	NVP(CT_EnvData),
174	NVP(CT_SignedEnvData),
175	NVP(CT_DigestData),
176	NVP(CT_EncryptedData),
177	NVP_END
178};
179
180static const p12NameValuePair p12BagTypeNames[] =
181{
182	NVP(BT_None),
183	NVP(BT_KeyBag),
184	NVP(BT_ShroudedKeyBag),
185	NVP(BT_CertBag),
186	NVP(BT_CrlBag),
187	NVP(BT_SecretBag),
188	NVP(BT_SafeContentsBag),
189	NVP_END
190};
191
192static const char *typeToStr(
193	unsigned type,
194	const p12NameValuePair *table)
195{
196	while(table->name) {
197		if(table->value == type) {
198			return table->name;
199		}
200		table++;
201	}
202	return "Unknown";
203}
204
205const char *p12BagTypeStr(
206	NSS_P12_SB_Type type)
207{
208	return typeToStr(type, p12BagTypeNames);
209}
210
211const char *p7ContentInfoTypeStr(
212	NSS_P7_CI_Type type)
213{
214	return typeToStr(type, p7CITypeNames);
215}
216
217/*
218 * OIDS for P12 and PKCS5 v1.5 (PBES1) encrypt and decrypt map to the following
219 * attributes.
220 */
221typedef struct {
222	const CSSM_OID		*oid;
223	CSSM_ALGORITHMS		keyAlg;		// e.g., CSSM_ALGID_DES
224	CSSM_ALGORITHMS		encrAlg;	// e.g., CSSM_ALGID_3DES_3KEY_EDE
225	CSSM_ALGORITHMS		pbeHashAlg;	// SHA1 or MD5
226	uint32				keySizeInBits;
227	uint32				blockSizeInBytes;	// for IV, optional
228	CSSM_PADDING		padding;	// CSSM_PADDING_PKCS7, etc.
229	CSSM_ENCRYPT_MODE	mode;		// CSSM_ALGMODE_CBCPadIV8, etc.
230	PKCS_Which			pkcs;		// PW_PKCS12 (for this module) or PW_PKCS5_v1_5
231} PKCSOidInfo;
232
233static const PKCSOidInfo pkcsOidInfos[] = {
234	/* PKCS12 first, the ones this module uses */
235	{
236		&CSSMOID_PKCS12_pbeWithSHAAnd128BitRC4,
237		CSSM_ALGID_RC4,
238		CSSM_ALGID_RC4,
239		CSSM_ALGID_SHA1,
240		128,
241		0,					// RC4 is a stream cipher
242		CSSM_PADDING_NONE,
243		CSSM_ALGMODE_NONE,
244		PW_PKCS12
245	},
246	{
247		&CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4,
248		CSSM_ALGID_RC4,
249		CSSM_ALGID_RC4,
250		CSSM_ALGID_SHA1,
251		40,
252		0,					// RC4 is a stream cipher
253		CSSM_PADDING_NONE,
254		CSSM_ALGMODE_NONE,
255		PW_PKCS12
256	},
257	{
258		&CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC,
259		CSSM_ALGID_3DES_3KEY,
260		CSSM_ALGID_3DES_3KEY_EDE,
261		CSSM_ALGID_SHA1,
262		64 * 3,
263		8,
264		CSSM_PADDING_PKCS7,
265		CSSM_ALGMODE_CBCPadIV8,
266		PW_PKCS12
267	},
268	{
269		&CSSMOID_PKCS12_pbeWithSHAAnd2Key3DESCBC,
270		CSSM_ALGID_3DES_2KEY,
271		CSSM_ALGID_3DES_2KEY_EDE,
272		CSSM_ALGID_SHA1,
273		64 * 2,
274		8,
275		CSSM_PADDING_PKCS7,
276		CSSM_ALGMODE_CBCPadIV8,
277		PW_PKCS12
278	},
279	{
280		&CSSMOID_PKCS12_pbeWithSHAAnd128BitRC2CBC,
281		CSSM_ALGID_RC2,
282		CSSM_ALGID_RC2,
283		CSSM_ALGID_SHA1,
284		128,
285		8,
286		CSSM_PADDING_PKCS7,
287		CSSM_ALGMODE_CBCPadIV8,
288		PW_PKCS12
289	},
290	{
291		&CSSMOID_PKCS12_pbewithSHAAnd40BitRC2CBC,
292		CSSM_ALGID_RC2,
293		CSSM_ALGID_RC2,
294		CSSM_ALGID_SHA1,
295		40,
296		8,
297		CSSM_PADDING_PKCS7,
298		CSSM_ALGMODE_CBCPadIV8,
299		PW_PKCS12
300	},
301
302	/* PKCS5 v1.5, used for SecImportExport module */
303	{
304		&CSSMOID_PKCS5_pbeWithMD2AndDES,
305		CSSM_ALGID_DES,
306		CSSM_ALGID_DES,
307		CSSM_ALGID_MD2,
308		64,
309		8,
310		CSSM_PADDING_PKCS7,
311		CSSM_ALGMODE_CBCPadIV8,
312		PW_PKCS5_v1_5
313	},
314	{
315		&CSSMOID_PKCS5_pbeWithMD2AndRC2,
316		CSSM_ALGID_RC2,
317		CSSM_ALGID_RC2,
318		CSSM_ALGID_MD2,
319		64,
320		8,
321		CSSM_PADDING_PKCS7,
322		CSSM_ALGMODE_CBCPadIV8,
323		PW_PKCS5_v1_5
324	},
325	{
326		&CSSMOID_PKCS5_pbeWithMD5AndDES,
327		CSSM_ALGID_DES,
328		CSSM_ALGID_DES,
329		CSSM_ALGID_MD5,
330		64,
331		8,
332		CSSM_PADDING_PKCS7,
333		CSSM_ALGMODE_CBCPadIV8,
334		PW_PKCS5_v1_5
335	},
336	{
337		&CSSMOID_PKCS5_pbeWithMD5AndRC2,
338		CSSM_ALGID_RC2,
339		CSSM_ALGID_RC2,
340		CSSM_ALGID_MD5,
341		64,
342		8,
343		CSSM_PADDING_PKCS7,
344		CSSM_ALGMODE_CBCPadIV8,
345		PW_PKCS5_v1_5
346	},
347	{
348		&CSSMOID_PKCS5_pbeWithSHA1AndDES,
349		CSSM_ALGID_DES,
350		CSSM_ALGID_DES,
351		CSSM_ALGID_SHA1,
352		64,
353		8,
354		CSSM_PADDING_PKCS7,
355		CSSM_ALGMODE_CBCPadIV8,
356		PW_PKCS5_v1_5
357	},
358	{
359		&CSSMOID_PKCS5_pbeWithSHA1AndRC2,
360		CSSM_ALGID_RC2,
361		CSSM_ALGID_RC2,
362		CSSM_ALGID_SHA1,
363		64,
364		8,
365		CSSM_PADDING_PKCS7,
366		CSSM_ALGMODE_CBCPadIV8,
367		PW_PKCS5_v1_5
368	},
369
370	/* finally one for PKCS5 v2.0, which has its own means of
371	 * cooking up all the parameters */
372	{
373		&CSSMOID_PKCS5_PBES2,
374		CSSM_ALGID_NONE,
375		CSSM_ALGID_NONE,
376		CSSM_ALGID_NONE,
377		0, 0, 0, 0,
378		PW_PKCS5_v2
379	}
380};
381
382#define NUM_PKCS_OID_INFOS (sizeof(pkcsOidInfos) / sizeof(pkcsOidInfos[1]))
383
384/* map an OID to the components */
385/* returns false if OID not found */
386
387/*
388 * NOTE: as of March 8 2004 this is also used by the SecImportExport
389 * module...not just PKCS12!
390 */
391bool pkcsOidToParams(
392	const CSSM_OID 		*oid,
393	CSSM_ALGORITHMS		&keyAlg,		// e.g., CSSM_ALGID_DES
394	CSSM_ALGORITHMS		&encrAlg,		// e.g., CSSM_ALGID_3DES_3KEY_EDE
395	CSSM_ALGORITHMS		&pbeHashAlg,	// SHA1 or MD5
396	uint32				&keySizeInBits,
397	uint32				&blockSizeInBytes,	// for IV, optional
398	CSSM_PADDING		&padding,		// CSSM_PADDING_PKCS7, etc.
399	CSSM_ENCRYPT_MODE	&mode,			// CSSM_ALGMODE_CBCPadIV8, etc.
400	PKCS_Which			&pkcs)			// PW_PKCS5_v1_5 or PW_PKCS12
401{
402	const PKCSOidInfo *info = pkcsOidInfos;
403	pkcs = PW_None;
404
405	for(unsigned dex=0; dex<NUM_PKCS_OID_INFOS; dex++) {
406		if(nssCompareCssmData(oid, info->oid)) {
407			keyAlg 			 = info->keyAlg;
408			encrAlg 		 = info->encrAlg;
409			pbeHashAlg 		 = info->pbeHashAlg;
410			keySizeInBits 	 = info->keySizeInBits;
411			blockSizeInBytes = info->blockSizeInBytes;
412			padding			 = info->padding;
413			mode 			 = info->mode;
414			pkcs			 = info->pkcs;
415			return true;
416		}
417		info++;
418	}
419	return false;
420}
421
422/*
423 * Verify MAC on an existing PFX.
424 */
425CSSM_RETURN p12VerifyMac(
426	const NSS_P12_DecodedPFX 	&pfx,
427	CSSM_CSP_HANDLE				cspHand,
428	const CSSM_DATA				*pwd,	// unicode, double null terminated
429	const CSSM_KEY				*passKey,
430	SecNssCoder					&coder)	// for temp mallocs
431{
432	if(pfx.macData == NULL) {
433		return CSSMERR_CSP_INVALID_SIGNATURE;
434	}
435	NSS_P12_MacData &macData = *pfx.macData;
436	NSS_P7_DigestInfo &digestInfo  = macData.mac;
437	CSSM_OID &algOid = digestInfo.digestAlgorithm.algorithm;
438	CSSM_ALGORITHMS macAlg;
439	if(!cssmOidToAlg(&algOid, &macAlg)) {
440		return CSSMERR_CSP_INVALID_ALGORITHM;
441	}
442	uint32 iterCount = 0;
443	CSSM_DATA &citer = macData.iterations;
444	if(!p12DataToInt(citer, iterCount)) {
445		return CSSMERR_CSP_INVALID_ATTR_ROUNDS;
446	}
447	if(iterCount == 0) {
448		/* optional, default 1 */
449		iterCount = 1;
450	}
451
452	/*
453	 * In classic fashion, the PKCS12 spec now says:
454	 *
455	 *      When password integrity mode is used to secure a PFX PDU,
456	 *      an SHA-1 HMAC is computed on the BER-encoding of the contents
457	 *      of the content field of the authSafe field in the PFX PDU.
458	 *
459	 * So here we go.
460	 */
461	CSSM_DATA genMac;
462	CSSM_RETURN crtn = p12GenMac(cspHand, *pfx.authSafe.content.data,
463		macAlg, iterCount, macData.macSalt, pwd, passKey, coder, genMac);
464	if(crtn) {
465		return crtn;
466	}
467	if(nssCompareCssmData(&genMac, &digestInfo.digest)) {
468		return CSSM_OK;
469	}
470	else {
471		return CSSMERR_CSP_VERIFY_FAILED;
472	}
473}
474
475/* we generate 8 random bytes of salt */
476#define P12_SALT_LEN		8
477
478void p12GenSalt(
479	CSSM_DATA &salt,
480	SecNssCoder &coder)
481{
482	DevRandomGenerator rng;
483	coder.allocItem(salt, P12_SALT_LEN);
484	rng.random(salt.Data, P12_SALT_LEN);
485}
486
487/*
488 * Generate random label string to allow associating an imported private
489 * key with a cert.
490 */
491void p12GenLabel(
492	CSSM_DATA &label,
493	SecNssCoder &coder)
494{
495	/* first a random uint32 */
496	uint8 d[4];
497	DevRandomGenerator rng;
498	rng.random(d, 4);
499	CSSM_DATA cd = {4, d};
500	uint32 i;
501	p12DataToInt(cd, i);
502
503	/* sprintf that into a real string */
504	coder.allocItem(label, 9);
505	memset(label.Data, 0, 9);
506	sprintf((char *)label.Data, "%08X", (unsigned)i);
507}
508
509/* NULL algorithm parameters */
510
511static const uint8 nullAlg[2] = {SEC_ASN1_NULL, 0};
512
513void p12NullAlgParams(
514	CSSM_X509_ALGORITHM_IDENTIFIER &algId)
515{
516	CSSM_DATA &p = algId.parameters;
517	p.Data = (uint8 *)nullAlg;
518	p.Length = 2;
519}
520
521/*
522 * Free memory via specified plugin's app-level allocator
523 */
524void freeCssmMemory(
525	CSSM_HANDLE	hand,
526	void 			*p)
527{
528	CSSM_API_MEMORY_FUNCS memFuncs;
529	CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
530	if(crtn) {
531		p12LogCssmError("CSSM_GetAPIMemoryFunctions", crtn);
532		/* oh well, leak and continue */
533		return;
534	}
535	memFuncs.free_func(p, memFuncs.AllocRef);
536}
537
538/*
539 * Find private key by label, modify its Label attr to be the
540 * hash of the associated public key.
541 * Also optionally re-sets the key's PrintName attribute; used to reset
542 * this attr from the random label we create when first unwrap it
543 * to the friendly name we find later after parsing attributes.
544 * Detection of a duplicate key when updating the key's attributes
545 * results in a lookup of the original key and returning it in
546 * foundKey.
547 */
548CSSM_RETURN p12SetPubKeyHash(
549	CSSM_CSP_HANDLE 	cspHand,		// where the key lives
550	CSSM_DL_DB_HANDLE 	dlDbHand,		// ditto
551	CSSM_DATA			&keyLabel,		// for DB lookup
552	CSSM_DATA_PTR		newPrintName,	// optional
553	SecNssCoder			&coder,			// for mallocing newLabel
554	CSSM_DATA			&newLabel,		// RETURNED with label as hash
555	CSSM_KEY_PTR		&foundKey)		// RETURNED
556{
557	CSSM_QUERY						query;
558	CSSM_SELECTION_PREDICATE		predicate;
559	CSSM_DB_UNIQUE_RECORD_PTR		record = NULL;
560	CSSM_RETURN						crtn;
561	CSSM_HANDLE						resultHand = 0;
562	CSSM_DATA						keyData = {0, NULL};
563	CSSM_CC_HANDLE					ccHand = 0;
564	CSSM_KEY_PTR					privKey = NULL;
565	CSSM_DATA_PTR					keyDigest = NULL;
566
567	assert(cspHand != 0);
568	query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
569	query.Conjunctive = CSSM_DB_NONE;
570	query.NumSelectionPredicates = 1;
571	predicate.DbOperator = CSSM_DB_EQUAL;
572
573	predicate.Attribute.Info.AttributeNameFormat =
574		CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
575	predicate.Attribute.Info.Label.AttributeName =
576		(char*) P12_KEY_ATTR_LABEL_AND_HASH;
577	predicate.Attribute.Info.AttributeFormat =
578		CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
579	/* hope this cast is OK */
580	predicate.Attribute.Value = &keyLabel;
581	query.SelectionPredicate = &predicate;
582
583	query.QueryLimits.TimeLimit = 0;	// FIXME - meaningful?
584	query.QueryLimits.SizeLimit = 1;	// FIXME - meaningful?
585	query.QueryFlags = CSSM_QUERY_RETURN_DATA;
586
587	/* build Record attribute with one or two attrs */
588	CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
589	CSSM_DB_ATTRIBUTE_DATA attr[2];
590	attr[0].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
591	attr[0].Info.Label.AttributeName = (char*) P12_KEY_ATTR_LABEL_AND_HASH;
592	attr[0].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
593	if(newPrintName) {
594		attr[1].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
595		attr[1].Info.Label.AttributeName = (char*) P12_KEY_ATTR_PRINT_NAME;
596		attr[1].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
597	}
598	recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
599	recordAttrs.NumberOfAttributes = newPrintName ? 2 : 1;
600	recordAttrs.AttributeData = attr;
601
602	crtn = CSSM_DL_DataGetFirst(dlDbHand,
603		&query,
604		&resultHand,
605		&recordAttrs,
606		&keyData,			// theData
607		&record);
608	/* abort only on success */
609	if(crtn != CSSM_OK) {
610		p12LogCssmError("CSSM_DL_DataGetFirst", crtn);
611		p12ErrorLog("***p12SetPubKeyHash: can't find private key\n");
612		return crtn;
613	}
614	/* subsequent errors to errOut: */
615	if(keyData.Data == NULL) {
616		p12ErrorLog("***p12SetPubKeyHash: private key lookup failure\n");
617		crtn = CSSMERR_CSSM_INTERNAL_ERROR;
618		goto errOut;
619	}
620	privKey = (CSSM_KEY_PTR)keyData.Data;
621
622	/* public key hash via passthrough - works on any key, any CSP/CSPDL.... */
623	/*
624	 * Warning! This relies on the current default ACL meaning "allow this
625	 * current app to access this private key" since we created the key.
626	 */
627	crtn = CSSM_CSP_CreatePassThroughContext(cspHand, privKey, &ccHand);
628	if(crtn) {
629		p12LogCssmError("CSSM_CSP_CreatePassThroughContext", crtn);
630		goto errOut;
631	}
632	crtn = CSSM_CSP_PassThrough(ccHand,
633		CSSM_APPLECSP_KEYDIGEST,
634		NULL,
635		(void **)&keyDigest);
636	if(crtn) {
637		p12LogCssmError("CSSM_CSP_PassThrough", crtn);
638		goto errOut;
639	}
640
641	/*
642	 * Replace Label attr data with hash.
643	 * NOTE: the module which allocated this attribute data - a DL -
644	 * was loaded and attached by out client layer, not by us. Thus
645	 * we can't use the memory allocator functions *we* used when
646	 * attaching to the CSP - we have to use the ones
647	 * which the client registered with the DL.
648	 */
649	freeCssmMemory(dlDbHand.DLHandle, attr[0].Value->Data);
650	freeCssmMemory(dlDbHand.DLHandle, attr[0].Value);
651	if(newPrintName) {
652		freeCssmMemory(dlDbHand.DLHandle, attr[1].Value->Data);
653		freeCssmMemory(dlDbHand.DLHandle, attr[1].Value);
654	}
655	/* modify key attributes */
656	attr[0].Value = keyDigest;
657	if(newPrintName) {
658		attr[1].Value = newPrintName;
659	}
660	crtn = CSSM_DL_DataModify(dlDbHand,
661			CSSM_DL_DB_RECORD_PRIVATE_KEY,
662			record,
663			&recordAttrs,
664            NULL,				// DataToBeModified
665			CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
666	switch(crtn) {
667		case CSSM_OK:
668			/* give caller the key's new label */
669			coder.allocCopyItem(*keyDigest, newLabel);
670			break;
671		default:
672			p12LogCssmError("CSSM_DL_DataModify", crtn);
673			break;
674		case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA:
675		{
676			/*
677			 * Special case: dup private key. The label we just tried to modify is
678			 * the public key hash so we can be confident that this really is a dup.
679			 * Delete it, look up the original, and return the original to caller.
680			 */
681			CSSM_RETURN drtn = CSSM_DL_DataDelete(dlDbHand, record);
682			if(drtn) {
683				p12LogCssmError("CSSM_DL_DataDelete on dup key", drtn);
684				crtn = drtn;
685				break;
686			}
687
688			/* Free items created in last search */
689			CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
690			resultHand = 0;
691			CSSM_DL_FreeUniqueRecord(dlDbHand, record);
692			record = NULL;
693
694			/* lookup by label as public key hash this time */
695			predicate.Attribute.Value = keyDigest;
696			drtn = CSSM_DL_DataGetFirst(dlDbHand,
697				&query,
698				&resultHand,
699				NULL,				// no attrs this time
700				&keyData,
701				&record);
702			if(drtn) {
703				p12LogCssmError("CSSM_DL_DataGetFirst on original key", crtn);
704				crtn = drtn;
705				break;
706			}
707			foundKey = (CSSM_KEY_PTR)keyData.Data;
708			/* give caller the key's actual label */
709			coder.allocCopyItem(*keyDigest, newLabel);
710			break;
711		}
712	}
713
714errOut:
715	/* free resources */
716	if(resultHand) {
717		CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
718	}
719	if(record) {
720		CSSM_DL_FreeUniqueRecord(dlDbHand, record);
721	}
722	if(ccHand) {
723		CSSM_DeleteContext(ccHand);
724	}
725	if(privKey) {
726		/* key created by the CSPDL */
727		CSSM_FreeKey(cspHand, NULL, privKey, CSSM_FALSE);
728		freeCssmMemory(dlDbHand.DLHandle, privKey);
729	}
730	if(keyDigest)  {
731		/* mallocd by someone else's CSP */
732		freeCssmMemory(cspHand, keyDigest->Data);
733		freeCssmMemory(cspHand, keyDigest);
734	}
735	return crtn;
736}
737
738/*
739 * Given a context specified via a CSSM_CC_HANDLE, add a new
740 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
741 * AttributeLength, and an untyped pointer.
742 */
743CSSM_RETURN p12AddContextAttribute(CSSM_CC_HANDLE CCHandle,
744	uint32 AttributeType,
745	uint32 AttributeLength,
746	const void *AttributePtr)
747{
748	CSSM_CONTEXT_ATTRIBUTE		newAttr;
749	CSSM_RETURN					crtn;
750
751	newAttr.AttributeType     = AttributeType;
752	newAttr.AttributeLength   = AttributeLength;
753	newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
754	crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
755	if(crtn) {
756		p12LogCssmError("CSSM_UpdateContextAttributes", crtn);
757	}
758	return crtn;
759}
760
761/*
762 * Find private key by specified label, delete it.
763 */
764CSSM_RETURN p12DeleteKey(
765	CSSM_DL_DB_HANDLE dlDbHand,
766	const CSSM_DATA	&keyLabel)
767{
768	CSSM_QUERY						query;
769	CSSM_SELECTION_PREDICATE		predicate;
770	CSSM_DB_UNIQUE_RECORD_PTR		record = NULL;
771	CSSM_RETURN						crtn;
772	CSSM_HANDLE						resultHand = 0;
773
774	query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
775	query.Conjunctive = CSSM_DB_NONE;
776	query.NumSelectionPredicates = 1;
777	predicate.DbOperator = CSSM_DB_EQUAL;
778
779	predicate.Attribute.Info.AttributeNameFormat =
780		CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
781	predicate.Attribute.Info.Label.AttributeName =
782		(char*) P12_KEY_ATTR_LABEL_AND_HASH;
783	predicate.Attribute.Info.AttributeFormat =
784		CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
785	predicate.Attribute.Value = const_cast<CSSM_DATA_PTR>(&keyLabel);
786
787	query.SelectionPredicate = &predicate;
788	query.QueryLimits.TimeLimit = 0;
789	query.QueryLimits.SizeLimit = 1;
790	query.QueryFlags = 0;
791
792	crtn = CSSM_DL_DataGetFirst(dlDbHand,
793		&query,
794		&resultHand,
795		NULL,			// attrs - don't need 'em
796		NULL, 			// theData - don't need it
797		&record);
798	/* abort only on success */
799	if(crtn) {
800		p12LogCssmError("CSSM_DL_DataGetFirst", crtn);
801		p12ErrorLog("***p12DeleteKey: can't find private key\n");
802		return crtn;
803	}
804
805	crtn = CSSM_DL_DataDelete(dlDbHand, record);
806	if(crtn) {
807		p12LogCssmError("CSSM_DL_DataDelete", crtn);
808		p12ErrorLog("***p12DeleteKey: can't delete private key\n");
809	}
810
811	CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
812	CSSM_DL_FreeUniqueRecord(dlDbHand, record);
813	return crtn;
814}
815
816/* convert App passphrase to array of chars used in P12 PBE */
817void p12ImportPassPhrase(
818	CFStringRef		inPhrase,
819	SecNssCoder		&coder,
820	CSSM_DATA		&outPhrase)
821{
822	CFDataRef cfData = CFStringCreateExternalRepresentation(NULL,
823		inPhrase, kCFStringEncodingUTF8, 0);
824	if(cfData == NULL) {
825		p12ErrorLog("***p12ImportPassPhrase: can't convert passphrase to UTF8\n");
826		MacOSError::throwMe(errSecParam);
827	}
828	CFIndex keyLen = CFDataGetLength(cfData);
829	coder.allocItem(outPhrase, keyLen);
830	memmove(outPhrase.Data, CFDataGetBytePtr(cfData), keyLen);
831	CFRelease(cfData);
832}
833