1/*
2 * Copyright (c) 2004,2013 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 * SecImportExportUtils.cpp - misc. utilities for import/export module
24 */
25
26#include "SecImportExportUtils.h"
27#include "SecImportExportAgg.h"
28#include "SecImportExportCrypto.h"
29#include "SecIdentityPriv.h"
30#include "SecItem.h"
31#include <security_cdsa_utils/cuCdsaUtils.h>
32#include <Security/SecBase.h>
33#pragma mark --- Debug support ---
34
35#ifndef NDEBUG
36
37const char *impExpExtFormatStr(
38	SecExternalFormat format)
39{
40	switch(format) {
41		case kSecFormatUnknown:			return "kSecFormatUnknown";
42		case kSecFormatOpenSSL:			return "kSecFormatOpenSSL";
43		case kSecFormatSSH:				return "kSecFormatSSH";
44		case kSecFormatBSAFE:			return "kSecFormatBSAFE";
45		case kSecFormatRawKey:			return "kSecFormatRawKey";
46		case kSecFormatWrappedPKCS8:	return "kSecFormatWrappedPKCS8";
47		case kSecFormatWrappedOpenSSL:  return "kSecFormatWrappedOpenSSL";
48		case kSecFormatWrappedSSH:		return "kSecFormatWrappedSSH";
49		case kSecFormatWrappedLSH:		return "kSecFormatWrappedLSH";
50		case kSecFormatX509Cert:		return "kSecFormatX509Cert";
51		case kSecFormatPEMSequence:		return "kSecFormatPEMSequence";
52		case kSecFormatPKCS7:			return "kSecFormatPKCS7";
53		case kSecFormatPKCS12:			return "kSecFormatPKCS12";
54		case kSecFormatNetscapeCertSequence:  return "kSecFormatNetscapeCertSequence";
55		default:						return "UNKNOWN FORMAT ENUM";
56	}
57}
58
59const char *impExpExtItemTypeStr(
60	SecExternalItemType itemType)
61{
62	switch(itemType) {
63		case kSecItemTypeUnknown:		return "kSecItemTypeUnknown";
64		case kSecItemTypePrivateKey:	return "kSecItemTypePrivateKey";
65		case kSecItemTypePublicKey:		return "kSecItemTypePublicKey";
66		case kSecItemTypeSessionKey:	return "kSecItemTypeSessionKey";
67		case kSecItemTypeCertificate:   return "kSecItemTypeCertificate";
68		case kSecItemTypeAggregate:		return "kSecItemTypeAggregate";
69		default:						return "UNKNOWN ITEM TYPE ENUM";
70	}
71}
72#endif  /* NDEBUG */
73
74/*
75 * Parse file extension and attempt to map it to format and type. Returns true
76 * on success.
77 */
78bool impExpImportParseFileExten(
79	CFStringRef			fstr,
80	SecExternalFormat   *inputFormat,   // RETURNED
81	SecExternalItemType	*itemType)		// RETURNED
82{
83	if(fstr == NULL) {
84		/* nothing to work with */
85		return false;
86	}
87	if(CFStringHasSuffix(fstr, CFSTR(".cer")) ||
88	   CFStringHasSuffix(fstr, CFSTR(".crt"))) {
89		*inputFormat = kSecFormatX509Cert;
90		*itemType = kSecItemTypeCertificate;
91		SecImpInferDbg("Inferring kSecFormatX509Cert from file name");
92		return true;
93	}
94	if(CFStringHasSuffix(fstr, CFSTR(".p12")) ||
95	   CFStringHasSuffix(fstr, CFSTR(".pfx"))) {
96		*inputFormat = kSecFormatPKCS12;
97		*itemType = kSecItemTypeAggregate;
98		SecImpInferDbg("Inferring kSecFormatPKCS12 from file name");
99		return true;
100	}
101
102	/* Get extension, look for key indicators as substrings */
103	CFURLRef url = CFURLCreateWithString(NULL, fstr, NULL);
104	if(url == NULL) {
105		SecImpInferDbg("impExpImportParseFileExten: error creating URL");
106		return false;
107	}
108	CFStringRef exten = CFURLCopyPathExtension(url);
109	CFRelease(url);
110	if(exten == NULL) {
111		/* no extension, app probably passed in only an extension */
112		exten = fstr;
113		CFRetain(exten);
114	}
115	bool ortn = false;
116	CFRange cfr;
117	cfr = CFStringFind(exten, CFSTR("p7"), kCFCompareCaseInsensitive);
118	if(cfr.length != 0) {
119		*inputFormat = kSecFormatPKCS7;
120		*itemType = kSecItemTypeAggregate;
121		SecImpInferDbg("Inferring kSecFormatPKCS7 from file name");
122		ortn = true;
123	}
124	if(!ortn) {
125		cfr = CFStringFind(exten, CFSTR("p8"), kCFCompareCaseInsensitive);
126		if(cfr.length != 0) {
127			*inputFormat = kSecFormatWrappedPKCS8;
128			*itemType = kSecItemTypePrivateKey;
129			SecImpInferDbg("Inferring kSecFormatPKCS8 from file name");
130			ortn = true;
131		}
132	}
133	CFRelease(exten);
134	return ortn;
135}
136
137/* do a [NSString stringByDeletingPathExtension] equivalent */
138CFStringRef impExpImportDeleteExtension(
139	CFStringRef			fileStr)
140{
141	CFDataRef fileStrData = CFStringCreateExternalRepresentation(NULL, fileStr,
142		kCFStringEncodingUTF8, 0);
143	if(fileStrData == NULL) {
144		return NULL;
145	}
146
147	CFURLRef urlRef = CFURLCreateFromFileSystemRepresentation(NULL,
148		CFDataGetBytePtr(fileStrData), CFDataGetLength(fileStrData), false);
149	if(urlRef == NULL) {
150		CFRelease(fileStrData);
151		return NULL;
152	}
153	CFURLRef rtnUrl = CFURLCreateCopyDeletingPathExtension(NULL, urlRef);
154	CFStringRef rtnStr = NULL;
155	CFRelease(urlRef);
156	if(rtnUrl) {
157		rtnStr = CFURLGetString(rtnUrl);
158		CFRetain(rtnStr);
159		CFRelease(rtnUrl);
160	}
161	CFRelease(fileStrData);
162	return rtnStr;
163}
164
165#pragma mark --- mapping of external format to CDSA formats ---
166
167/*
168 * For the record, here is the mapping of SecExternalFormat, algorithm, and key
169 * class to CSSM-style key format (CSSM_KEYBLOB_FORMAT -
170 * CSSM_KEYBLOB_RAW_FORMAT_X509, etc). The entries in the table are the
171 * last component of a CSSM_KEYBLOB_FORMAT. Format kSecFormatUnknown means
172 * "default for specified class and algorithm", which is currently the
173 * same as kSecFormatOpenSSL.
174 *
175 *							              algorithm/class
176 *						      RSA			    DSA				   DH
177 *						----------------  ----------------  ----------------
178 * SecExternalFormat	 priv      pub     priv      pub	 priv      pub
179 * -----------------	-------  -------  -------  -------  -------  -------
180 * kSecFormatOpenSSL	 PKCS1    X509    OPENSSL   X509     PKCS3    X509
181 * kSecFormatBSAFE       PKCS8    PKCS1   FIPS186   FIPS186  PKCS8    not supported
182 * kSecFormatUnknown	 PKCS1    X509    OPENSSL   X509     PKCS3    X509
183 * kSecFormatSSH		  SSH      SSH      n/s     n/s       n/s     n/s
184 * kSecFormatSSHv2        n/s     SSH2      n/s     SSH2      n/s     n/s
185 *
186 * The only external format supported for ECDSA and ECDH keys is kSecFormatOpenSSL,
187 * which translates to OPENSSL for private keys and X509 for public keys.
188 */
189
190/* Arrays expressing the above table. */
191
192/* l.s. dimension is pub/priv for one alg */
193typedef struct {
194	CSSM_KEYBLOB_FORMAT priv;
195	CSSM_KEYBLOB_FORMAT pub;
196} algForms;
197
198/*
199 * indices into array of algForms defining all algs' formats for a given
200 * SecExternalFormat
201 */
202#define SIE_ALG_RSA		0
203#define SIE_ALG_DSA		1
204#define SIE_ALG_DH		2
205#define SIE_ALG_ECDSA	3
206#define SIE_ALG_LAST	SIE_ALG_ECDSA
207#define SIE_NUM_ALGS	(SIE_ALG_LAST + 1)
208
209/* kSecFormatOpenSSL */
210static algForms opensslAlgForms[SIE_NUM_ALGS] =
211{
212	{ CSSM_KEYBLOB_RAW_FORMAT_PKCS1,	CSSM_KEYBLOB_RAW_FORMAT_X509 },		// RSA
213	{ CSSM_KEYBLOB_RAW_FORMAT_OPENSSL,  CSSM_KEYBLOB_RAW_FORMAT_X509 },		// DSA
214	{ CSSM_KEYBLOB_RAW_FORMAT_PKCS3,	CSSM_KEYBLOB_RAW_FORMAT_X509 },		// D-H
215	{ CSSM_KEYBLOB_RAW_FORMAT_OPENSSL,	CSSM_KEYBLOB_RAW_FORMAT_X509 },		// ECDSA
216};
217
218/* kSecFormatBSAFE */
219static algForms bsafeAlgForms[SIE_NUM_ALGS] =
220{
221	{ CSSM_KEYBLOB_RAW_FORMAT_PKCS8,	CSSM_KEYBLOB_RAW_FORMAT_PKCS1 },	// RSA
222	{ CSSM_KEYBLOB_RAW_FORMAT_FIPS186,  CSSM_KEYBLOB_RAW_FORMAT_FIPS186 },	// DSA
223	{ CSSM_KEYBLOB_RAW_FORMAT_PKCS8,	CSSM_KEYBLOB_RAW_FORMAT_NONE },		// D-H
224	{ CSSM_KEYBLOB_RAW_FORMAT_NONE,		CSSM_KEYBLOB_RAW_FORMAT_NONE },		// ECDSA not supported
225};
226
227/* kSecFormatSSH (v1) */
228static algForms ssh1AlgForms[SIE_NUM_ALGS] =
229{
230	{ CSSM_KEYBLOB_RAW_FORMAT_OPENSSH,	CSSM_KEYBLOB_RAW_FORMAT_OPENSSH },	// RSA only
231	{ CSSM_KEYBLOB_RAW_FORMAT_NONE,		CSSM_KEYBLOB_RAW_FORMAT_NONE },		// DSA not supported
232	{ CSSM_KEYBLOB_RAW_FORMAT_NONE,		CSSM_KEYBLOB_RAW_FORMAT_NONE },		// D-H not supported
233	{ CSSM_KEYBLOB_RAW_FORMAT_NONE,		CSSM_KEYBLOB_RAW_FORMAT_NONE },		// ECDSA not supported
234};
235
236/* kSecFormatSSHv2 */
237static algForms ssh2AlgForms[SIE_NUM_ALGS] =
238{
239	{ CSSM_KEYBLOB_RAW_FORMAT_NONE,		CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2 },	// RSA - public only
240	{ CSSM_KEYBLOB_RAW_FORMAT_NONE,		CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2 },	// DSA - public only
241	{ CSSM_KEYBLOB_RAW_FORMAT_NONE,		CSSM_KEYBLOB_RAW_FORMAT_NONE },		// D-H not supported
242	{ CSSM_KEYBLOB_RAW_FORMAT_NONE,		CSSM_KEYBLOB_RAW_FORMAT_NONE },		// ECDSA not supported
243};
244
245/*
246 * This routine performs a lookup into the above 3-dimensional array to
247 * map {algorithm, class, SecExternalFormat} to a CSSM_KEYBLOB_FORMAT.
248 * Returns errSecUnsupportedFormat in the rare appropriate case.
249 */
250OSStatus impExpKeyForm(
251	SecExternalFormat		externForm,
252	SecExternalItemType		itemType,
253	CSSM_ALGORITHMS			alg,
254	CSSM_KEYBLOB_FORMAT		*cssmForm,		// RETURNED
255	CSSM_KEYCLASS			*cssmClass)		// RETRUNED
256{
257	if(itemType == kSecItemTypeSessionKey) {
258		/* special trivial case */
259		/* FIXME ensure caller hasn't specified bogus format */
260		*cssmForm = CSSM_KEYBLOB_RAW_FORMAT_NONE;
261		*cssmClass = CSSM_KEYCLASS_SESSION_KEY;
262		return errSecSuccess;
263	}
264	if(externForm == kSecFormatUnknown) {
265		/* default is openssl */
266		externForm = kSecFormatOpenSSL;
267	}
268
269	unsigned algDex;
270	switch(alg) {
271		case CSSM_ALGID_RSA:
272			algDex = SIE_ALG_RSA;
273			break;
274		case CSSM_ALGID_DSA:
275			algDex = SIE_ALG_DSA;
276			break;
277		case CSSM_ALGID_DH:
278			algDex = SIE_ALG_DH;
279			break;
280		case CSSM_ALGID_ECDSA:
281			algDex = SIE_ALG_ECDSA;
282			break;
283		default:
284			return CSSMERR_CSP_INVALID_ALGORITHM;
285	}
286	const algForms *forms;
287	switch(externForm) {
288		case kSecFormatOpenSSL:
289			forms = opensslAlgForms;
290			break;
291		case kSecFormatBSAFE:
292			forms = bsafeAlgForms;
293			break;
294		case kSecFormatSSH:
295			forms = ssh1AlgForms;
296			break;
297		case kSecFormatSSHv2:
298			forms = ssh2AlgForms;
299			break;
300		default:
301			return errSecUnsupportedFormat;
302	}
303	CSSM_KEYBLOB_FORMAT form = CSSM_KEYBLOB_RAW_FORMAT_NONE;
304	switch(itemType) {
305		case kSecItemTypePrivateKey:
306			form = forms[algDex].priv;
307			*cssmClass = CSSM_KEYCLASS_PRIVATE_KEY;
308			break;
309		case kSecItemTypePublicKey:
310			form = forms[algDex].pub;
311			*cssmClass = CSSM_KEYCLASS_PUBLIC_KEY;
312			break;
313		default:
314			return errSecUnsupportedFormat;
315	}
316	if(form == CSSM_KEYBLOB_RAW_FORMAT_NONE) {
317		/* not in the tables - abort */
318		return errSecUnsupportedFormat;
319	}
320	else {
321		*cssmForm = form;
322		return errSecSuccess;
323	}
324}
325
326/*
327 * Given a raw key blob and zero to three known parameters (type, format,
328 * algorithm), figure out all parameters. Used for private and public keys.
329 */
330static bool impExpGuessKeyParams(
331	CFDataRef				keyData,
332	SecExternalFormat		*externForm,		// IN/OUT
333	SecExternalItemType		*itemType,			// IN/OUT
334	CSSM_ALGORITHMS			*keyAlg)			// IN/OUT
335{
336	/* CSSM alg list: RSA, DSA, DH, ECDSA */
337	CSSM_ALGORITHMS minAlg		 = CSSM_ALGID_RSA;
338	CSSM_ALGORITHMS maxAlg		 = CSSM_ALGID_ECDSA;
339	SecExternalFormat minForm    = kSecFormatOpenSSL;		// then SSH, BSAFE, then...
340	SecExternalFormat maxForm    = kSecFormatSSHv2;
341	SecExternalItemType minType  = kSecItemTypePrivateKey;  // just two
342	SecExternalItemType maxType  = kSecItemTypePublicKey;
343
344	switch(*externForm) {
345		case kSecFormatUnknown:
346			break;								// run through all formats
347		case kSecFormatOpenSSL:
348		case kSecFormatSSH:
349		case kSecFormatSSHv2:
350		case kSecFormatBSAFE:
351			minForm = maxForm = *externForm;	// just test this one
352			break;
353		default:
354			return false;
355	}
356	switch(*itemType) {
357		case kSecItemTypeUnknown:
358			break;
359		case kSecItemTypePrivateKey:
360		case kSecItemTypePublicKey:
361			minType = maxType = *itemType;
362			break;
363		default:
364			return false;
365	}
366	switch(*keyAlg) {
367		case CSSM_ALGID_NONE:
368			break;
369		case CSSM_ALGID_RSA:
370		case CSSM_ALGID_DSA:
371		case CSSM_ALGID_DH:
372		case CSSM_ALGID_ECDSA:
373			minAlg = maxAlg = *keyAlg;
374			break;
375		default:
376			return false;
377	}
378
379	CSSM_ALGORITHMS theAlg;
380	SecExternalFormat theForm;
381	SecExternalItemType theType;
382	CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_TRUE);
383	if(cspHand == 0) {
384		return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
385	}
386
387	/*
388	 * Iterate through all set of enabled {alg, type, format}.
389	 * We do not assume that any of the enums are sequential hence this
390	 * odd iteration algorithm....
391	 */
392	bool ourRtn = false;
393	for(theAlg=minAlg; ; ) {
394		for(theForm=minForm; ; ) {
395			for(theType=minType; ; ) {
396
397				/* do super lightweight null unwrap to parse */
398				OSStatus ortn = impExpImportRawKey(keyData,
399					theForm, theType, theAlg,
400					NULL,		// no keychain
401					cspHand,
402					0,			// no flags
403					NULL,		// no key params
404					NULL,		// no printName
405					NULL);		// no returned items
406				if(ortn == errSecSuccess) {
407					*externForm = theForm;
408					*itemType = theType;
409					*keyAlg = theAlg;
410					ourRtn = true;
411					goto done;
412				}
413
414				/* next type or break if we're done */
415				if(theType == maxType) {
416					break;
417				}
418				else switch(theType) {
419					case kSecItemTypePrivateKey:
420						theType = kSecItemTypePublicKey;
421						break;
422					default:
423						assert(0);
424						ourRtn = false;
425						goto done;
426				}
427			}   /* for each class/type */
428
429			/* next format or break if we're done */
430			if(theForm == maxForm) {
431				break;
432			}
433			else switch(theForm) {
434				case kSecFormatOpenSSL:
435					theForm = kSecFormatSSH;
436					break;
437				case kSecFormatSSH:
438					theForm = kSecFormatBSAFE;
439					break;
440				case kSecFormatBSAFE:
441					theForm = kSecFormatSSHv2;
442					break;
443				default:
444					assert(0);
445					ourRtn = false;
446					goto done;
447			}
448		}		/* for each format */
449
450		/* next alg or break if we're done */
451		if(theAlg == maxAlg) {
452			break;
453		}
454		else switch(theAlg) {
455			case CSSM_ALGID_RSA:
456				theAlg = CSSM_ALGID_DSA;
457				break;
458			case CSSM_ALGID_DSA:
459				theAlg = CSSM_ALGID_DH;
460				break;
461			case CSSM_ALGID_DH:
462				theAlg = CSSM_ALGID_ECDSA;
463				break;
464			default:
465				/* i.e. CSSM_ALGID_ECDSA, we already broke at theAlg == maxAlg */
466				assert(0);
467				ourRtn = false;
468				goto done;
469		}
470	}			/* for each alg */
471done:
472	cuCspDetachUnload(cspHand, CSSM_TRUE);
473	return ourRtn;
474}
475
476/*
477 * Guess an incoming blob's type, format and (for keys only) algorithm
478 * by examining its contents. Returns true on success, in which case
479 * *inputFormat, *itemType, and *keyAlg are all valid. Caller optionally
480 * passes in valid values any number of these as a clue.
481 */
482bool impExpImportGuessByExamination(
483	CFDataRef			inData,
484	SecExternalFormat   *inputFormat,	// may be kSecFormatUnknown on entry
485	SecExternalItemType	*itemType,		// may be kSecItemTypeUnknown on entry
486	CSSM_ALGORITHMS		*keyAlg)		// CSSM_ALGID_NONE - unknown
487{
488	if( ( (*inputFormat == kSecFormatUnknown) ||
489	      (*inputFormat == kSecFormatX509Cert)
490		) &&
491	   ( (*itemType == kSecItemTypeUnknown) ||
492		 (*itemType == kSecItemTypeCertificate) ) ) {
493		/*
494		 * See if it parses as a cert
495		 */
496		CSSM_CL_HANDLE clHand = cuClStartup();
497		if(clHand == 0) {
498			return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
499		}
500		CSSM_HANDLE cacheHand;
501		CSSM_RETURN crtn;
502		CSSM_DATA cdata = { CFDataGetLength(inData),
503						    (uint8 *)CFDataGetBytePtr(inData) };
504		crtn = CSSM_CL_CertCache(clHand, &cdata, &cacheHand);
505		bool brtn = false;
506		if(crtn == CSSM_OK) {
507			*inputFormat = kSecFormatX509Cert;
508			*itemType = kSecItemTypeCertificate;
509			SecImpInferDbg("Inferred kSecFormatX509Cert via CL");
510			CSSM_CL_CertAbortCache(clHand, cacheHand);
511			brtn = true;
512		}
513		cuClDetachUnload(clHand);
514		if(brtn) {
515			return true;
516		}
517	}
518	/* TBD: need way to inquire of P12 lib if this is a valid-looking PFX */
519
520	if( ( (*inputFormat == kSecFormatUnknown) ||
521	      (*inputFormat == kSecFormatNetscapeCertSequence)
522		) &&
523	   ( (*itemType == kSecItemTypeUnknown) ||
524		 (*itemType == kSecItemTypeAggregate) ) ) {
525		/* See if it's a netscape cert sequence */
526		CSSM_RETURN crtn = impExpNetscapeCertImport(inData, 0, NULL, NULL, NULL);
527		if(crtn == CSSM_OK) {
528			*inputFormat = kSecFormatNetscapeCertSequence;
529			*itemType = kSecItemTypeAggregate;
530			SecImpInferDbg("Inferred netscape-cert-sequence by decoding");
531			return true;
532		}
533	}
534
535	/* See if it's a key */
536	return impExpGuessKeyParams(inData, inputFormat, itemType, keyAlg);
537}
538
539#pragma mark --- Key Import support ---
540
541/*
542 * Given a context specified via a CSSM_CC_HANDLE, add a new
543 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
544 * AttributeLength, and an untyped pointer.
545 */
546CSSM_RETURN impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle,
547	uint32 AttributeType,
548	uint32 AttributeLength,
549	const void *AttributePtr)
550{
551	CSSM_CONTEXT_ATTRIBUTE		newAttr;
552
553	newAttr.AttributeType     = AttributeType;
554	newAttr.AttributeLength   = AttributeLength;
555	newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
556	return CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
557}
558
559/*
560 * Free memory via specified plugin's app-level allocator
561 */
562void impExpFreeCssmMemory(
563	CSSM_HANDLE		hand,
564	void 			*p)
565{
566	CSSM_API_MEMORY_FUNCS memFuncs;
567	CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
568	if(crtn) {
569		return;
570	}
571	memFuncs.free_func(p, memFuncs.AllocRef);
572}
573
574/*
575 * Calculate digest of any CSSM_KEY. Unlike older implementations
576 * of this logic, you can actually calculate the public key hash
577 * on any class of key, any format, raw CSP or CSPDL (though if
578 * you're using the CSPDL, the key has to be a reference key
579 * in that CSPDL session).
580 *
581 * Caller must free keyDigest->Data using impExpFreeCssmMemory() since
582 * this is allocated by the CSP's app-specified allocator.
583 */
584CSSM_RETURN impExpKeyDigest(
585	CSSM_CSP_HANDLE cspHand,
586	CSSM_KEY_PTR	key,
587	CSSM_DATA_PTR   keyDigest)		// contents allocd and RETURNED
588{
589	CSSM_DATA_PTR   localDigest;
590	CSSM_CC_HANDLE  ccHand;
591
592	CSSM_RETURN crtn = CSSM_CSP_CreatePassThroughContext(cspHand,
593		key,
594		&ccHand);
595	if(crtn) {
596		return crtn;
597	}
598	crtn = CSSM_CSP_PassThrough(ccHand,
599		CSSM_APPLECSP_KEYDIGEST,
600		NULL,
601		(void **)&localDigest);
602	if(crtn) {
603		SecImpExpDbg("CSSM_CSP_PassThrough(KEY_DIGEST) failure");
604	}
605	else {
606		/*
607		 * Give caller the Data referent and we'll free the
608		 * CSSM_DATA struct itswelf.
609		 */
610		*keyDigest = *localDigest;
611		impExpFreeCssmMemory(cspHand, localDigest);
612	}
613	CSSM_DeleteContext(ccHand);
614	return crtn;
615}
616
617
618/*
619 * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef,
620 * return a refcounted CFStringRef suitable for use with the PKCS12 library.
621 * PKCS12 passphrases in CFData format must be UTF8 encoded.
622 */
623OSStatus impExpPassphraseToCFString(
624	CFTypeRef   passin,
625	CFStringRef *passout)	// may be the same as passin, but refcounted
626{
627	if(CFGetTypeID(passin) == CFStringGetTypeID()) {
628		CFStringRef passInStr = (CFStringRef)passin;
629		CFRetain(passInStr);
630		*passout = passInStr;
631		return errSecSuccess;
632	}
633	else if(CFGetTypeID(passin) == CFDataGetTypeID()) {
634		CFDataRef cfData = (CFDataRef)passin;
635		CFIndex len = CFDataGetLength(cfData);
636		CFStringRef cfStr = CFStringCreateWithBytes(NULL,
637			CFDataGetBytePtr(cfData), len, kCFStringEncodingUTF8, true);
638		if(cfStr == NULL) {
639			SecImpExpDbg("Passphrase not in UTF8 format");
640			return errSecParam;
641		}
642		*passout = cfStr;
643		return errSecSuccess;
644	}
645	else {
646		SecImpExpDbg("Passphrase not CFData or CFString");
647		return errSecParam;
648	}
649}
650
651/*
652 * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef,
653 * return a refcounted CFDataRef whose bytes are suitable for use with
654 * PKCS5 (v1.5 and v2.0) key derivation.
655 */
656OSStatus impExpPassphraseToCFData(
657	CFTypeRef   passin,
658	CFDataRef   *passout)	// may be the same as passin, but refcounted
659{
660	if(CFGetTypeID(passin) == CFDataGetTypeID()) {
661		CFDataRef passInData = (CFDataRef)passin;
662		CFRetain(passInData);
663		*passout = passInData;
664		return errSecSuccess;
665	}
666	else if(CFGetTypeID(passin) == CFStringGetTypeID()) {
667		CFStringRef passInStr = (CFStringRef)passin;
668		CFDataRef outData;
669		outData = CFStringCreateExternalRepresentation(NULL,
670			passInStr,
671			kCFStringEncodingUTF8,
672			0);		// lossByte 0 ==> no loss allowed
673		if(outData == NULL) {
674			/* Well try with lossy conversion */
675			SecImpExpDbg("Trying lossy conversion of CFString passphrase to UTF8");
676			outData = CFStringCreateExternalRepresentation(NULL,
677				passInStr,
678				kCFStringEncodingUTF8,
679				1);
680			if(outData == NULL) {
681				SecImpExpDbg("Failure on conversion of CFString passphrase to UTF8");
682				/* what do we do now, Batman? */
683				return errSecParam;
684			}
685		}
686		*passout = outData;
687		return errSecSuccess;
688	}
689	else {
690		SecImpExpDbg("Passphrase not CFData or CFString");
691		return errSecParam;
692	}
693}
694
695/*
696* Add a CFString to a crypto context handle.
697*/
698static CSSM_RETURN impExpAddStringAttr(
699	CSSM_CC_HANDLE ccHand,
700	CFStringRef str,
701	CSSM_ATTRIBUTE_TYPE attrType)
702{
703	/* CFStrings are passed as external rep in UTF8 encoding by convention */
704	CFDataRef outData;
705	outData = CFStringCreateExternalRepresentation(NULL,
706		str, kCFStringEncodingUTF8,	0);		// lossByte 0 ==> no loss allowed
707	if(outData == NULL) {
708		SecImpExpDbg("impExpAddStringAttr: bad string format");
709		return errSecParam;
710	}
711
712	CSSM_DATA attrData;
713	attrData.Data = (uint8 *)CFDataGetBytePtr(outData);
714	attrData.Length = CFDataGetLength(outData);
715	CSSM_RETURN crtn = impExpAddContextAttribute(ccHand, attrType, sizeof(CSSM_DATA),
716		&attrData);
717	CFRelease(outData);
718	if(crtn) {
719		SecImpExpDbg("impExpAddStringAttr: CSSM_UpdateContextAttributes error");
720	}
721	return crtn;
722}
723
724/*
725 * Generate a secure passphrase key. Caller must eventually CSSM_FreeKey the result.
726 */
727static CSSM_RETURN impExpCreatePassKey(
728	const SecKeyImportExportParameters *keyParams,  // required
729	CSSM_CSP_HANDLE		cspHand,		// MUST be CSPDL
730	impExpVerifyPhrase	verifyPhrase,   // for secure passphrase
731	CSSM_KEY_PTR		*passKey)		// mallocd and RETURNED
732{
733	CSSM_RETURN crtn;
734	CSSM_CC_HANDLE ccHand;
735	uint32 verifyAttr;
736	CSSM_DATA dummyLabel;
737	CSSM_KEY_PTR ourKey = NULL;
738
739	SecImpExpDbg("Generating secure passphrase key");
740	ourKey = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY));
741	if(ourKey == NULL) {
742		return errSecAllocate;
743	}
744	memset(ourKey, 0, sizeof(CSSM_KEY));
745
746	crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
747		CSSM_ALGID_SECURE_PASSPHRASE,
748		4,				// keySizeInBits must be non zero
749		NULL,			// Seed
750		NULL,			// Salt
751		NULL,			// StartDate
752		NULL,			// EndDate
753		NULL,			// Params
754		&ccHand);
755	if(crtn) {
756		SecImpExpDbg("impExpCreatePassKey: CSSM_CSP_CreateKeyGenContext error");
757		return crtn;
758	}
759	/* subsequent errors to errOut: */
760
761	/* additional context attributes specific to this type of key gen */
762	assert(keyParams != NULL);			// or we wouldn't be here
763	if(keyParams->alertTitle != NULL) {
764		crtn = impExpAddStringAttr(ccHand, keyParams->alertTitle,
765			CSSM_ATTRIBUTE_ALERT_TITLE);
766		if(crtn) {
767			goto errOut;
768		}
769	}
770	if(keyParams->alertPrompt != NULL) {
771		crtn = impExpAddStringAttr(ccHand, keyParams->alertPrompt,
772			CSSM_ATTRIBUTE_PROMPT);
773		if(crtn) {
774			goto errOut;
775		}
776	}
777	verifyAttr = (verifyPhrase == VP_Export) ? 1 : 0;
778	crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_VERIFY_PASSPHRASE,
779		sizeof(uint32), (const void *)((size_t) verifyAttr));
780	if(crtn) {
781		SecImpExpDbg("impExpCreatePassKey: CSSM_UpdateContextAttributes error");
782		goto errOut;
783	}
784
785	dummyLabel.Data = (uint8 *)"Secure Passphrase";
786	dummyLabel.Length = strlen((char *)dummyLabel.Data);
787
788	crtn = CSSM_GenerateKey(ccHand,
789		CSSM_KEYUSE_ANY,
790		CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE,
791		&dummyLabel,
792		NULL,			// ACL
793		ourKey);
794	if(crtn) {
795		SecImpExpDbg("impExpCreatePassKey: CSSM_GenerateKey error");
796	}
797errOut:
798	CSSM_DeleteContext(ccHand);
799	if(crtn == CSSM_OK) {
800		*passKey = ourKey;
801	}
802	else if(ourKey != NULL) {
803		free(ourKey);
804	}
805	return crtn;
806}
807
808/*
809 * Obtain passphrase, given a SecKeyImportExportParameters.
810 *
811 * Passphrase comes from one of two places: app-specified, in
812 * SecKeyImportExportParameters.passphrase (either as CFStringRef
813 * or CFDataRef); or via the secure passphrase mechanism.
814 *
815 * Passphrase is returned in AT MOST one of two forms:
816 *
817 * -- Secure passphrase is returned as a CSSM_KEY_PTR, which the
818 *    caller must CSSM_FreeKey later as well as free()ing the actual
819 *    CSSM_KEY_PTR.
820 * -- CFTypeRef for app-supplied passphrases. This can be one of
821 *    two types:
822 *
823 *    -- CFStringRef, for use with P12
824 *    -- CFDataRef, for more general use (e.g. for PKCS5).
825 *
826 *    In either case the caller must CFRelease the result.
827 */
828OSStatus impExpPassphraseCommon(
829	const SecKeyImportExportParameters *keyParams,
830	CSSM_CSP_HANDLE			cspHand,		// MUST be CSPDL, for passKey generation
831	impExpPassphraseForm	phraseForm,
832	impExpVerifyPhrase		verifyPhrase,   // for secure passphrase
833	CFTypeRef				*phrase,		// RETURNED, or
834	CSSM_KEY_PTR			*passKey)		// mallocd and RETURNED
835{
836	assert(keyParams != NULL);
837
838	/* Give precedence to secure passphrase */
839	if(keyParams->flags & kSecKeySecurePassphrase) {
840		assert(passKey != NULL);
841		return impExpCreatePassKey(keyParams, cspHand, verifyPhrase, passKey);
842	}
843	else if(keyParams->passphrase != NULL) {
844		CFTypeRef phraseOut;
845		OSStatus ortn;
846		assert(phrase != NULL);
847		switch(phraseForm) {
848			case SPF_String:
849				ortn = impExpPassphraseToCFString(keyParams->passphrase,
850					(CFStringRef *)&phraseOut);
851				break;
852			case SPF_Data:
853				ortn = impExpPassphraseToCFData(keyParams->passphrase,
854					(CFDataRef *)&phraseOut);
855				break;
856			default:
857				/* only called internally */
858				assert(0);
859				ortn = errSecParam;
860		}
861		if(ortn == errSecSuccess) {
862			*phrase = phraseOut;
863		}
864		return ortn;
865	}
866	else {
867		return errSecPassphraseRequired;
868	}
869}
870
871static void ToggleKeyAttribute(
872	CFArrayRef keyAttrs,
873	CFTypeRef attr,
874	CSSM_KEYATTR_FLAGS mask,
875	CSSM_KEYATTR_FLAGS &result)
876{
877	// If the keyAttrs array contains the given attribute,
878	// set the corresponding keyattr flags, otherwise clear them.
879	// (Note: caller verifies that keyAttrs is not NULL.)
880	CFIndex numItems = CFArrayGetCount(keyAttrs);
881	result &= ~(mask);
882	if (numItems > 0) {
883		CFRange range = CFRangeMake(0, numItems);
884		if (CFArrayContainsValue(keyAttrs, range, attr))
885			result |= mask;
886	}
887}
888
889CSSM_KEYATTR_FLAGS ConvertArrayToKeyAttributes(SecKeyRef aKey, CFArrayRef keyAttrs)
890{
891	CSSM_KEYATTR_FLAGS result = CSSM_KEYATTR_RETURN_DEFAULT;
892
893	if (aKey) {
894		const CSSM_KEY* cssmKey = NULL;
895		if (errSecSuccess == SecKeyGetCSSMKey(aKey, &cssmKey))
896			result = cssmKey->KeyHeader.KeyAttr;
897	}
898
899	if (!keyAttrs)
900		return result;
901
902	CFMutableArrayRef attrs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
903	CFIndex idx, count = CFArrayGetCount(keyAttrs);
904	for (idx=0; idx<count; idx++) {
905		CFTypeRef attr = (CFTypeRef) CFArrayGetValueAtIndex(keyAttrs, idx);
906		if (attr && (CFNumberGetTypeID() == CFGetTypeID(attr))) {
907			// Convert numeric CSSM keyattr values to equivalent attribute constants
908			uint32 value;
909			if (CFNumberGetValue((CFNumberRef)attr, kCFNumberSInt32Type, &value)) {
910				switch (value) {
911					case CSSM_KEYATTR_SENSITIVE:
912						attr = kSecAttrIsSensitive;
913						break;
914					case CSSM_KEYATTR_EXTRACTABLE:
915						attr = kSecAttrIsExtractable;
916						break;
917					case CSSM_KEYATTR_PERMANENT:
918						attr = kSecAttrIsPermanent;
919						break;
920					default:
921						attr = NULL;
922						break;
923				}
924			}
925		}
926		if (attr)
927			CFArrayAppendValue(attrs, attr);
928	}
929
930	// Set key attribute flag in result if present in the array, otherwise clear
931	ToggleKeyAttribute(attrs, kSecAttrIsSensitive, CSSM_KEYATTR_SENSITIVE, result);
932	ToggleKeyAttribute(attrs, kSecAttrIsExtractable, CSSM_KEYATTR_EXTRACTABLE, result);
933	ToggleKeyAttribute(attrs, kSecAttrIsPermanent, CSSM_KEYATTR_PERMANENT, result);
934
935	// if caller specified an attributes array which omitted kSecAttrIsExtractable,
936	// this implies the sensitive attribute; force it on so that at least one bit
937	// is set. If our result is 0, this is indistinguishable from the case where
938	// no key attributes were specified, causing a default bitmask to be used
939	// in subsequent import code.
940
941	if (0==(result & CSSM_KEYATTR_EXTRACTABLE))
942		result |= CSSM_KEYATTR_SENSITIVE;
943
944	CFRelease(attrs);
945	return result;
946}
947
948Boolean ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(SecKeyRef aKey,
949	const SecItemImportExportKeyParameters* newPtr, SecKeyImportExportParameters* oldPtr)
950{
951	Boolean result = false;
952
953	if (NULL != oldPtr && NULL != newPtr)
954	{
955		oldPtr->version = newPtr->version;
956		oldPtr->flags = newPtr->flags;
957		oldPtr->passphrase = newPtr->passphrase;
958		oldPtr->alertTitle = newPtr->alertTitle;
959		oldPtr->alertPrompt = newPtr->alertPrompt;
960		oldPtr->accessRef = newPtr->accessRef;
961		oldPtr->keyUsage = ConvertArrayToKeyUsage(newPtr->keyUsage);
962		oldPtr->keyAttributes = ConvertArrayToKeyAttributes(aKey, newPtr->keyAttributes);
963		result = true;
964	}
965	return result;
966}
967
968