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