1/*
2 * Copyright (c) 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 * 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
345    if(keyData == NULL || CFDataGetLength(keyData) == 0){
346                MacOSError::throwMe(errSecUnsupportedKeySize);
347    }
348
349	switch(*externForm) {
350		case kSecFormatUnknown:
351			break;								// run through all formats
352		case kSecFormatOpenSSL:
353		case kSecFormatSSH:
354		case kSecFormatSSHv2:
355		case kSecFormatBSAFE:
356			minForm = maxForm = *externForm;	// just test this one
357			break;
358		default:
359			return false;
360	}
361	switch(*itemType) {
362		case kSecItemTypeUnknown:
363			break;
364		case kSecItemTypePrivateKey:
365		case kSecItemTypePublicKey:
366			minType = maxType = *itemType;
367			break;
368		default:
369			return false;
370	}
371	switch(*keyAlg) {
372		case CSSM_ALGID_NONE:
373			break;
374		case CSSM_ALGID_RSA:
375		case CSSM_ALGID_DSA:
376		case CSSM_ALGID_DH:
377		case CSSM_ALGID_ECDSA:
378			minAlg = maxAlg = *keyAlg;
379			break;
380		default:
381			return false;
382	}
383
384	CSSM_ALGORITHMS theAlg;
385	SecExternalFormat theForm;
386	SecExternalItemType theType;
387	CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_TRUE);
388	if(cspHand == 0) {
389		return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
390	}
391
392	/*
393	 * Iterate through all set of enabled {alg, type, format}.
394	 * We do not assume that any of the enums are sequential hence this
395	 * odd iteration algorithm....
396	 */
397	bool ourRtn = false;
398	for(theAlg=minAlg; ; ) {
399		for(theForm=minForm; ; ) {
400			for(theType=minType; ; ) {
401
402				/* do super lightweight null unwrap to parse */
403				OSStatus ortn = impExpImportRawKey(keyData,
404					theForm, theType, theAlg,
405					NULL,		// no keychain
406					cspHand,
407					0,			// no flags
408					NULL,		// no key params
409					NULL,		// no printName
410					NULL);		// no returned items
411				if(ortn == errSecSuccess) {
412					*externForm = theForm;
413					*itemType = theType;
414					*keyAlg = theAlg;
415					ourRtn = true;
416					goto done;
417				}
418
419				/* next type or break if we're done */
420				if(theType == maxType) {
421					break;
422				}
423				else switch(theType) {
424					case kSecItemTypePrivateKey:
425						theType = kSecItemTypePublicKey;
426						break;
427					default:
428						assert(0);
429						ourRtn = false;
430						goto done;
431				}
432			}   /* for each class/type */
433
434			/* next format or break if we're done */
435			if(theForm == maxForm) {
436				break;
437			}
438			else switch(theForm) {
439				case kSecFormatOpenSSL:
440					theForm = kSecFormatSSH;
441					break;
442				case kSecFormatSSH:
443					theForm = kSecFormatBSAFE;
444					break;
445				case kSecFormatBSAFE:
446					theForm = kSecFormatSSHv2;
447					break;
448				default:
449					assert(0);
450					ourRtn = false;
451					goto done;
452			}
453		}		/* for each format */
454
455		/* next alg or break if we're done */
456		if(theAlg == maxAlg) {
457			break;
458		}
459		else switch(theAlg) {
460			case CSSM_ALGID_RSA:
461				theAlg = CSSM_ALGID_DSA;
462				break;
463			case CSSM_ALGID_DSA:
464				theAlg = CSSM_ALGID_DH;
465				break;
466			case CSSM_ALGID_DH:
467				theAlg = CSSM_ALGID_ECDSA;
468				break;
469			default:
470				/* i.e. CSSM_ALGID_ECDSA, we already broke at theAlg == maxAlg */
471				assert(0);
472				ourRtn = false;
473				goto done;
474		}
475	}			/* for each alg */
476done:
477	cuCspDetachUnload(cspHand, CSSM_TRUE);
478	return ourRtn;
479}
480
481/*
482 * Guess an incoming blob's type, format and (for keys only) algorithm
483 * by examining its contents. Returns true on success, in which case
484 * *inputFormat, *itemType, and *keyAlg are all valid. Caller optionally
485 * passes in valid values any number of these as a clue.
486 */
487bool impExpImportGuessByExamination(
488	CFDataRef			inData,
489	SecExternalFormat   *inputFormat,	// may be kSecFormatUnknown on entry
490	SecExternalItemType	*itemType,		// may be kSecItemTypeUnknown on entry
491	CSSM_ALGORITHMS		*keyAlg)		// CSSM_ALGID_NONE - unknown
492{
493	if( ( (*inputFormat == kSecFormatUnknown) ||
494	      (*inputFormat == kSecFormatX509Cert)
495		) &&
496	   ( (*itemType == kSecItemTypeUnknown) ||
497		 (*itemType == kSecItemTypeCertificate) ) ) {
498		/*
499		 * See if it parses as a cert
500		 */
501		CSSM_CL_HANDLE clHand = cuClStartup();
502		if(clHand == 0) {
503			return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
504		}
505		CSSM_HANDLE cacheHand;
506		CSSM_RETURN crtn;
507		CSSM_DATA cdata = { CFDataGetLength(inData),
508						    (uint8 *)CFDataGetBytePtr(inData) };
509		crtn = CSSM_CL_CertCache(clHand, &cdata, &cacheHand);
510		bool brtn = false;
511		if(crtn == CSSM_OK) {
512			*inputFormat = kSecFormatX509Cert;
513			*itemType = kSecItemTypeCertificate;
514			SecImpInferDbg("Inferred kSecFormatX509Cert via CL");
515			CSSM_CL_CertAbortCache(clHand, cacheHand);
516			brtn = true;
517		}
518		cuClDetachUnload(clHand);
519		if(brtn) {
520			return true;
521		}
522	}
523	/* TBD: need way to inquire of P12 lib if this is a valid-looking PFX */
524
525	if( ( (*inputFormat == kSecFormatUnknown) ||
526	      (*inputFormat == kSecFormatNetscapeCertSequence)
527		) &&
528	   ( (*itemType == kSecItemTypeUnknown) ||
529		 (*itemType == kSecItemTypeAggregate) ) ) {
530		/* See if it's a netscape cert sequence */
531		CSSM_RETURN crtn = impExpNetscapeCertImport(inData, 0, NULL, NULL, NULL);
532		if(crtn == CSSM_OK) {
533			*inputFormat = kSecFormatNetscapeCertSequence;
534			*itemType = kSecItemTypeAggregate;
535			SecImpInferDbg("Inferred netscape-cert-sequence by decoding");
536			return true;
537		}
538	}
539
540	/* See if it's a key */
541	return impExpGuessKeyParams(inData, inputFormat, itemType, keyAlg);
542}
543
544#pragma mark --- Key Import support ---
545
546/*
547 * Given a context specified via a CSSM_CC_HANDLE, add a new
548 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
549 * AttributeLength, and an untyped pointer.
550 */
551CSSM_RETURN impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle,
552	uint32 AttributeType,
553	uint32 AttributeLength,
554	const void *AttributePtr)
555{
556	CSSM_CONTEXT_ATTRIBUTE		newAttr;
557
558	newAttr.AttributeType     = AttributeType;
559	newAttr.AttributeLength   = AttributeLength;
560	newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
561	return CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
562}
563
564/*
565 * Free memory via specified plugin's app-level allocator
566 */
567void impExpFreeCssmMemory(
568	CSSM_HANDLE		hand,
569	void 			*p)
570{
571	CSSM_API_MEMORY_FUNCS memFuncs;
572	CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
573	if(crtn) {
574		return;
575	}
576	memFuncs.free_func(p, memFuncs.AllocRef);
577}
578
579/*
580 * Calculate digest of any CSSM_KEY. Unlike older implementations
581 * of this logic, you can actually calculate the public key hash
582 * on any class of key, any format, raw CSP or CSPDL (though if
583 * you're using the CSPDL, the key has to be a reference key
584 * in that CSPDL session).
585 *
586 * Caller must free keyDigest->Data using impExpFreeCssmMemory() since
587 * this is allocated by the CSP's app-specified allocator.
588 */
589CSSM_RETURN impExpKeyDigest(
590	CSSM_CSP_HANDLE cspHand,
591	CSSM_KEY_PTR	key,
592	CSSM_DATA_PTR   keyDigest)		// contents allocd and RETURNED
593{
594	CSSM_DATA_PTR   localDigest;
595	CSSM_CC_HANDLE  ccHand;
596
597	CSSM_RETURN crtn = CSSM_CSP_CreatePassThroughContext(cspHand,
598		key,
599		&ccHand);
600	if(crtn) {
601		return crtn;
602	}
603	crtn = CSSM_CSP_PassThrough(ccHand,
604		CSSM_APPLECSP_KEYDIGEST,
605		NULL,
606		(void **)&localDigest);
607	if(crtn) {
608		SecImpExpDbg("CSSM_CSP_PassThrough(KEY_DIGEST) failure");
609	}
610	else {
611		/*
612		 * Give caller the Data referent and we'll free the
613		 * CSSM_DATA struct itswelf.
614		 */
615		*keyDigest = *localDigest;
616		impExpFreeCssmMemory(cspHand, localDigest);
617	}
618	CSSM_DeleteContext(ccHand);
619	return crtn;
620}
621
622
623/*
624 * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef,
625 * return a refcounted CFStringRef suitable for use with the PKCS12 library.
626 * PKCS12 passphrases in CFData format must be UTF8 encoded.
627 */
628OSStatus impExpPassphraseToCFString(
629	CFTypeRef   passin,
630	CFStringRef *passout)	// may be the same as passin, but refcounted
631{
632	if(CFGetTypeID(passin) == CFStringGetTypeID()) {
633		CFStringRef passInStr = (CFStringRef)passin;
634		CFRetain(passInStr);
635		*passout = passInStr;
636		return errSecSuccess;
637	}
638	else if(CFGetTypeID(passin) == CFDataGetTypeID()) {
639		CFDataRef cfData = (CFDataRef)passin;
640		CFIndex len = CFDataGetLength(cfData);
641		CFStringRef cfStr = CFStringCreateWithBytes(NULL,
642			CFDataGetBytePtr(cfData), len, kCFStringEncodingUTF8, true);
643		if(cfStr == NULL) {
644			SecImpExpDbg("Passphrase not in UTF8 format");
645			return errSecParam;
646		}
647		*passout = cfStr;
648		return errSecSuccess;
649	}
650	else {
651		SecImpExpDbg("Passphrase not CFData or CFString");
652		return errSecParam;
653	}
654}
655
656/*
657 * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef,
658 * return a refcounted CFDataRef whose bytes are suitable for use with
659 * PKCS5 (v1.5 and v2.0) key derivation.
660 */
661OSStatus impExpPassphraseToCFData(
662	CFTypeRef   passin,
663	CFDataRef   *passout)	// may be the same as passin, but refcounted
664{
665	if(CFGetTypeID(passin) == CFDataGetTypeID()) {
666		CFDataRef passInData = (CFDataRef)passin;
667		CFRetain(passInData);
668		*passout = passInData;
669		return errSecSuccess;
670	}
671	else if(CFGetTypeID(passin) == CFStringGetTypeID()) {
672		CFStringRef passInStr = (CFStringRef)passin;
673		CFDataRef outData;
674		outData = CFStringCreateExternalRepresentation(NULL,
675			passInStr,
676			kCFStringEncodingUTF8,
677			0);		// lossByte 0 ==> no loss allowed
678		if(outData == NULL) {
679			/* Well try with lossy conversion */
680			SecImpExpDbg("Trying lossy conversion of CFString passphrase to UTF8");
681			outData = CFStringCreateExternalRepresentation(NULL,
682				passInStr,
683				kCFStringEncodingUTF8,
684				1);
685			if(outData == NULL) {
686				SecImpExpDbg("Failure on conversion of CFString passphrase to UTF8");
687				/* what do we do now, Batman? */
688				return errSecParam;
689			}
690		}
691		*passout = outData;
692		return errSecSuccess;
693	}
694	else {
695		SecImpExpDbg("Passphrase not CFData or CFString");
696		return errSecParam;
697	}
698}
699
700/*
701* Add a CFString to a crypto context handle.
702*/
703static CSSM_RETURN impExpAddStringAttr(
704	CSSM_CC_HANDLE ccHand,
705	CFStringRef str,
706	CSSM_ATTRIBUTE_TYPE attrType)
707{
708	/* CFStrings are passed as external rep in UTF8 encoding by convention */
709	CFDataRef outData;
710	outData = CFStringCreateExternalRepresentation(NULL,
711		str, kCFStringEncodingUTF8,	0);		// lossByte 0 ==> no loss allowed
712	if(outData == NULL) {
713		SecImpExpDbg("impExpAddStringAttr: bad string format");
714		return errSecParam;
715	}
716
717	CSSM_DATA attrData;
718	attrData.Data = (uint8 *)CFDataGetBytePtr(outData);
719	attrData.Length = CFDataGetLength(outData);
720	CSSM_RETURN crtn = impExpAddContextAttribute(ccHand, attrType, sizeof(CSSM_DATA),
721		&attrData);
722	CFRelease(outData);
723	if(crtn) {
724		SecImpExpDbg("impExpAddStringAttr: CSSM_UpdateContextAttributes error");
725	}
726	return crtn;
727}
728
729/*
730 * Generate a secure passphrase key. Caller must eventually CSSM_FreeKey the result.
731 */
732static CSSM_RETURN impExpCreatePassKey(
733	const SecKeyImportExportParameters *keyParams,  // required
734	CSSM_CSP_HANDLE		cspHand,		// MUST be CSPDL
735	impExpVerifyPhrase	verifyPhrase,   // for secure passphrase
736	CSSM_KEY_PTR		*passKey)		// mallocd and RETURNED
737{
738	CSSM_RETURN crtn;
739	CSSM_CC_HANDLE ccHand;
740	uint32 verifyAttr;
741	CSSM_DATA dummyLabel;
742	CSSM_KEY_PTR ourKey = NULL;
743
744	SecImpExpDbg("Generating secure passphrase key");
745	ourKey = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY));
746	if(ourKey == NULL) {
747		return errSecAllocate;
748	}
749	memset(ourKey, 0, sizeof(CSSM_KEY));
750
751	crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
752		CSSM_ALGID_SECURE_PASSPHRASE,
753		4,				// keySizeInBits must be non zero
754		NULL,			// Seed
755		NULL,			// Salt
756		NULL,			// StartDate
757		NULL,			// EndDate
758		NULL,			// Params
759		&ccHand);
760	if(crtn) {
761		SecImpExpDbg("impExpCreatePassKey: CSSM_CSP_CreateKeyGenContext error");
762		return crtn;
763	}
764	/* subsequent errors to errOut: */
765
766	/* additional context attributes specific to this type of key gen */
767	assert(keyParams != NULL);			// or we wouldn't be here
768	if(keyParams->alertTitle != NULL) {
769		crtn = impExpAddStringAttr(ccHand, keyParams->alertTitle,
770			CSSM_ATTRIBUTE_ALERT_TITLE);
771		if(crtn) {
772			goto errOut;
773		}
774	}
775	if(keyParams->alertPrompt != NULL) {
776		crtn = impExpAddStringAttr(ccHand, keyParams->alertPrompt,
777			CSSM_ATTRIBUTE_PROMPT);
778		if(crtn) {
779			goto errOut;
780		}
781	}
782	verifyAttr = (verifyPhrase == VP_Export) ? 1 : 0;
783	crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_VERIFY_PASSPHRASE,
784		sizeof(uint32), (const void *)((size_t) verifyAttr));
785	if(crtn) {
786		SecImpExpDbg("impExpCreatePassKey: CSSM_UpdateContextAttributes error");
787		goto errOut;
788	}
789
790	dummyLabel.Data = (uint8 *)"Secure Passphrase";
791	dummyLabel.Length = strlen((char *)dummyLabel.Data);
792
793	crtn = CSSM_GenerateKey(ccHand,
794		CSSM_KEYUSE_ANY,
795		CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE,
796		&dummyLabel,
797		NULL,			// ACL
798		ourKey);
799	if(crtn) {
800		SecImpExpDbg("impExpCreatePassKey: CSSM_GenerateKey error");
801	}
802errOut:
803	CSSM_DeleteContext(ccHand);
804	if(crtn == CSSM_OK) {
805		*passKey = ourKey;
806	}
807	else if(ourKey != NULL) {
808		free(ourKey);
809	}
810	return crtn;
811}
812
813/*
814 * Obtain passphrase, given a SecKeyImportExportParameters.
815 *
816 * Passphrase comes from one of two places: app-specified, in
817 * SecKeyImportExportParameters.passphrase (either as CFStringRef
818 * or CFDataRef); or via the secure passphrase mechanism.
819 *
820 * Passphrase is returned in AT MOST one of two forms:
821 *
822 * -- Secure passphrase is returned as a CSSM_KEY_PTR, which the
823 *    caller must CSSM_FreeKey later as well as free()ing the actual
824 *    CSSM_KEY_PTR.
825 * -- CFTypeRef for app-supplied passphrases. This can be one of
826 *    two types:
827 *
828 *    -- CFStringRef, for use with P12
829 *    -- CFDataRef, for more general use (e.g. for PKCS5).
830 *
831 *    In either case the caller must CFRelease the result.
832 */
833OSStatus impExpPassphraseCommon(
834	const SecKeyImportExportParameters *keyParams,
835	CSSM_CSP_HANDLE			cspHand,		// MUST be CSPDL, for passKey generation
836	impExpPassphraseForm	phraseForm,
837	impExpVerifyPhrase		verifyPhrase,   // for secure passphrase
838	CFTypeRef				*phrase,		// RETURNED, or
839	CSSM_KEY_PTR			*passKey)		// mallocd and RETURNED
840{
841	assert(keyParams != NULL);
842
843	/* Give precedence to secure passphrase */
844	if(keyParams->flags & kSecKeySecurePassphrase) {
845		assert(passKey != NULL);
846		return impExpCreatePassKey(keyParams, cspHand, verifyPhrase, passKey);
847	}
848	else if(keyParams->passphrase != NULL) {
849		CFTypeRef phraseOut;
850		OSStatus ortn;
851		assert(phrase != NULL);
852		switch(phraseForm) {
853			case SPF_String:
854				ortn = impExpPassphraseToCFString(keyParams->passphrase,
855					(CFStringRef *)&phraseOut);
856				break;
857			case SPF_Data:
858				ortn = impExpPassphraseToCFData(keyParams->passphrase,
859					(CFDataRef *)&phraseOut);
860				break;
861			default:
862				/* only called internally */
863				assert(0);
864				ortn = errSecParam;
865		}
866		if(ortn == errSecSuccess) {
867			*phrase = phraseOut;
868		}
869		return ortn;
870	}
871	else {
872		return errSecPassphraseRequired;
873	}
874}
875
876static void ToggleKeyAttribute(
877	CFArrayRef keyAttrs,
878	CFTypeRef attr,
879	CSSM_KEYATTR_FLAGS mask,
880	CSSM_KEYATTR_FLAGS &result)
881{
882	// If the keyAttrs array contains the given attribute,
883	// set the corresponding keyattr flags, otherwise clear them.
884	// (Note: caller verifies that keyAttrs is not NULL.)
885	CFIndex numItems = CFArrayGetCount(keyAttrs);
886	result &= ~(mask);
887	if (numItems > 0) {
888		CFRange range = CFRangeMake(0, numItems);
889		if (CFArrayContainsValue(keyAttrs, range, attr))
890			result |= mask;
891	}
892}
893
894CSSM_KEYATTR_FLAGS ConvertArrayToKeyAttributes(SecKeyRef aKey, CFArrayRef keyAttrs)
895{
896	CSSM_KEYATTR_FLAGS result = CSSM_KEYATTR_RETURN_DEFAULT;
897
898	if (aKey) {
899		const CSSM_KEY* cssmKey = NULL;
900		if (errSecSuccess == SecKeyGetCSSMKey(aKey, &cssmKey))
901			result = cssmKey->KeyHeader.KeyAttr;
902	}
903
904	if (!keyAttrs)
905		return result;
906
907	CFMutableArrayRef attrs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
908	CFIndex idx, count = CFArrayGetCount(keyAttrs);
909	for (idx=0; idx<count; idx++) {
910		CFTypeRef attr = (CFTypeRef) CFArrayGetValueAtIndex(keyAttrs, idx);
911		if (attr && (CFNumberGetTypeID() == CFGetTypeID(attr))) {
912			// Convert numeric CSSM keyattr values to equivalent attribute constants
913			uint32 value;
914			if (CFNumberGetValue((CFNumberRef)attr, kCFNumberSInt32Type, &value)) {
915				switch (value) {
916					case CSSM_KEYATTR_SENSITIVE:
917						attr = kSecAttrIsSensitive;
918						break;
919					case CSSM_KEYATTR_EXTRACTABLE:
920						attr = kSecAttrIsExtractable;
921						break;
922					case CSSM_KEYATTR_PERMANENT:
923						attr = kSecAttrIsPermanent;
924						break;
925					default:
926						attr = NULL;
927						break;
928				}
929			}
930		}
931		if (attr)
932			CFArrayAppendValue(attrs, attr);
933	}
934
935	// Set key attribute flag in result if present in the array, otherwise clear
936	ToggleKeyAttribute(attrs, kSecAttrIsSensitive, CSSM_KEYATTR_SENSITIVE, result);
937	ToggleKeyAttribute(attrs, kSecAttrIsExtractable, CSSM_KEYATTR_EXTRACTABLE, result);
938	ToggleKeyAttribute(attrs, kSecAttrIsPermanent, CSSM_KEYATTR_PERMANENT, result);
939
940	// if caller specified an attributes array which omitted kSecAttrIsExtractable,
941	// this implies the sensitive attribute; force it on so that at least one bit
942	// is set. If our result is 0, this is indistinguishable from the case where
943	// no key attributes were specified, causing a default bitmask to be used
944	// in subsequent import code.
945
946	if (0==(result & CSSM_KEYATTR_EXTRACTABLE))
947		result |= CSSM_KEYATTR_SENSITIVE;
948
949	CFRelease(attrs);
950	return result;
951}
952
953Boolean ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(SecKeyRef aKey,
954	const SecItemImportExportKeyParameters* newPtr, SecKeyImportExportParameters* oldPtr)
955{
956	Boolean result = false;
957
958	if (NULL != oldPtr && NULL != newPtr)
959	{
960		oldPtr->version = newPtr->version;
961		oldPtr->flags = newPtr->flags;
962		oldPtr->passphrase = newPtr->passphrase;
963		oldPtr->alertTitle = newPtr->alertTitle;
964		oldPtr->alertPrompt = newPtr->alertPrompt;
965		oldPtr->accessRef = newPtr->accessRef;
966		oldPtr->keyUsage = ConvertArrayToKeyUsage(newPtr->keyUsage);
967		oldPtr->keyAttributes = ConvertArrayToKeyAttributes(aKey, newPtr->keyAttributes);
968		result = true;
969	}
970	return result;
971}
972
973