/ OSX / libsecurity_keychain / lib / SecExternalRep.cpp
SecExternalRep.cpp
  1  /*
  2   * Copyright (c) 2004,2011,2013-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   * SecExternalRep.cpp - private class representing an external representation of
 24   *					    a SecKeychainItemRef, used by SecImportExport.h
 25   */
 26  
 27  #include "SecExternalRep.h"
 28  #include "SecImportExportPem.h"
 29  #include "SecImportExportAgg.h"
 30  #include "SecImportExportUtils.h"
 31  #include "SecImportExportPkcs8.h"
 32  #include "SecImportExportCrypto.h"
 33  #include "SecImportExportOpenSSH.h"
 34  #include <security_utilities/errors.h>
 35  #include <Security/SecBase.h>
 36  #include <Security/SecKeyPriv.h>
 37  #include <Security/SecCertificate.h>
 38  #include <Security/cssmapi.h>
 39  
 40  using namespace Security;
 41  using namespace KeychainCore;
 42  
 43  
 44  #pragma mark --- SecExportRep Subclasses seen only by SecExportRep::vend() ---
 45  
 46  namespace SecExport {
 47  
 48  class Key : public SecExportRep 
 49  {
 50  	friend class SecExportRep;
 51  protected:
 52  	Key(
 53  		CFTypeRef						kcItemRef);
 54  	~Key();
 55  	OSStatus exportRep(
 56  		SecExternalFormat					format,	
 57  		SecItemImportExportFlags			flags,	
 58  		const SecKeyImportExportParameters	*keyParams,		// optional 
 59  		CFMutableDataRef					outData,		// data appended here
 60  		const char							**pemHeader);   // e.g., "RSA PUBLIC KEY"
 61  		
 62  private:
 63  	CSSM_ALGORITHMS						mKeyAlg;	
 64  	const CSSM_KEY						*mCssmKey;	
 65  };
 66  
 67  class Cert : public SecExportRep 
 68  {
 69  	friend class SecExportRep;
 70  protected:
 71  	Cert(
 72  		CFTypeRef						kcItemRef);
 73  	~Cert();
 74  	OSStatus exportRep(
 75  		SecExternalFormat					format,	
 76  		SecItemImportExportFlags			flags,	
 77  		const SecKeyImportExportParameters	*keyParams,		// optional 
 78  		CFMutableDataRef					outData,		// data appended here
 79  		const char							**pemHeader);   // e.g., "CERTIFICATE"
 80  };
 81  
 82  }   /* namespace SecExport */
 83  
 84  #pragma mark --- SecExportRep: Representation of an internal object on export ---
 85  
 86  SecExportRep::SecExportRep(
 87  	CFTypeRef			kcItemRef) :
 88  		mKcItem((SecKeychainItemRef)kcItemRef),
 89  		mPemParamLines(NULL)
 90  {
 91  	CFRetain(mKcItem);
 92  }
 93  
 94  SecExportRep::~SecExportRep()
 95  {
 96  	if(mKcItem) {
 97  		CFRelease(mKcItem);
 98  	}
 99  	if(mPemParamLines) {
100  		CFRelease(mPemParamLines);
101  	}
102  }
103  
104  SecExportRep::SecExportRep() {
105  	MacOSError::throwMe(errSecInvalidItemRef);
106  }
107  
108  /* must be implemented by subclass */
109  OSStatus SecExportRep::exportRep(
110  	SecExternalFormat					format,	
111  	SecItemImportExportFlags			flags,	
112  	const SecKeyImportExportParameters	*keyParams,		// optional 
113  	CFMutableDataRef					outData,		// data appended here
114  	const char							**pemHeader)	// e.g., "X509 CERTIFICATE"
115  {
116  	MacOSError::throwMe(errSecInvalidItemRef);
117  }
118  
119  /*
120   * Sole public means of obtaining a SecExportRep object. In fact only instances
121   * of subclasses are vended but caller does not know that.
122   * 
123   * Gleans SecExternalItemType from incoming type, throws MacOSError if
124   * incoming type is bogus.
125   *
126   * Vended object holds a reference to kcItem for its lifetime. 
127   */
128  SecExportRep *SecExportRep::vend(	
129  	CFTypeRef						kcItemRef)
130  {
131  	CFTypeID itemType = CFGetTypeID(kcItemRef);
132  	if(itemType == SecCertificateGetTypeID()) {
133  		return new SecExport::Cert(kcItemRef);
134  	}
135  	else if(itemType == SecKeyGetTypeID()) {
136  		return new SecExport::Key(kcItemRef);
137  	}
138  	else {
139  		MacOSError::throwMe(errSecInvalidItemRef);
140  	}
141  }
142  
143  #pragma mark --- Key External rep ---
144  
145  SecExport::Key::Key(
146  	CFTypeRef kcItemRef) :
147  		SecExportRep(kcItemRef)
148  {
149  	
150  	/* figure out if it's public, private, or session */
151  	OSStatus ortn;
152  	ortn = SecKeyGetCSSMKey((SecKeyRef)kcItemRef, &mCssmKey);
153  	if(ortn) {
154  		SecImpExpDbg("SecKeyGetCSSMKey failure in SecExportRep::Key()");
155  		MacOSError::throwMe(ortn);
156  	}
157  	switch(mCssmKey->KeyHeader.KeyClass) {
158  		case CSSM_KEYCLASS_PUBLIC_KEY:
159  			mExternType = kSecItemTypePublicKey;
160  			SecImpExpDbg("SecExportRep::Key(): SET_PubKey");
161  			break;
162  		case CSSM_KEYCLASS_PRIVATE_KEY:
163  			mExternType = kSecItemTypePrivateKey;
164  			SecImpExpDbg("SecExportRep::Key(): SET_PrivKey");
165  			break;
166  		case CSSM_KEYCLASS_SESSION_KEY:
167  			mExternType = kSecItemTypeSessionKey;
168  			SecImpExpDbg("SecExportRep::Key(): SET_SessionKey");
169  			break;
170  		default:
171  			SecImpExpDbg("SecExportRep::Key(): invalid KeyClass (%lu)",  
172  				(unsigned long)mCssmKey->KeyHeader.KeyClass);
173  			MacOSError::throwMe(errSecInvalidItemRef);
174  	}
175  	mKeyAlg = mCssmKey->KeyHeader.AlgorithmId;
176  }
177  
178  SecExport::Key::~Key()
179  {
180  	/* nothing for now */
181  }
182  
183  /* 
184   * The heart of this class: cook up external representation, appending to 
185   * existing CFMutableDataRef.
186   */
187  OSStatus SecExport::Key::exportRep(
188  	SecExternalFormat					format,	
189  	SecItemImportExportFlags			flags,			
190  	const SecKeyImportExportParameters	*keyParams,	// optional 
191  	CFMutableDataRef					outData,	// data appended here
192  	const char							**pemHeader)// e.g., "X509 CERTIFICATE"
193  {
194  	assert(outData != NULL);
195  	assert(mKcItem != NULL);
196  	assert(mCssmKey != NULL);
197  		
198  	/*
199  	 * Currently only OpsnSSH formats allow for a DescriptiveData field
200  	 * in either wrapped or NULL wrap forms. (In OpenSSH parlance this is 
201  	 * the 'comment' field). Infer the DescriptiveData to be embedded
202  	 * in the exported key from the item's PrintName attribute.
203  	 */
204  	CssmAutoData descrData(Allocator::standard());
205  	switch(format) {
206  		case kSecFormatSSH:
207  		case kSecFormatSSHv2:
208  		case kSecFormatWrappedSSH:
209  			impExpOpensshInferDescData((SecKeyRef)mKcItem, descrData);
210  			break;
211  		default:
212  			break;
213  	}
214  	
215  	/* 
216  	 * Handle wrapped key formats. 
217  	 */
218  	switch(format) {
219  		case kSecFormatWrappedPKCS8:
220  			return impExpPkcs8Export((SecKeyRef)mKcItem, flags, keyParams,
221  				outData, pemHeader);
222  		case kSecFormatWrappedOpenSSL:
223  			return impExpWrappedKeyOpenSslExport((SecKeyRef)mKcItem, flags, keyParams,
224  				outData, pemHeader, &mPemParamLines);
225  		case kSecFormatWrappedSSH:
226  			return impExpWrappedOpenSSHExport((SecKeyRef)mKcItem, flags, keyParams, 
227  				descrData, outData);
228  		case kSecFormatWrappedLSH:
229  			return errSecUnsupportedFormat;
230  		default:
231  			break;
232  	}
233  	
234  	/* 
235  	 * Remaining formats just do a NULL key wrap. Figure out the appropriate
236  	 * CDSA-specific format and wrap parameters. 
237  	 */
238  	OSStatus ortn = errSecSuccess;
239  	CSSM_KEYBLOB_FORMAT blobForm;
240  	
241  	switch(mExternType) {
242  		case kSecItemTypePublicKey:
243  			switch(mKeyAlg) {
244  				case CSSM_ALGID_RSA:
245  					*pemHeader = PEM_STRING_RSA_PUBLIC;
246  					break;  
247  				case CSSM_ALGID_DH:
248  					*pemHeader = PEM_STRING_DH_PUBLIC;
249  					break;  
250  				case CSSM_ALGID_DSA:
251  					*pemHeader = PEM_STRING_DSA_PUBLIC;
252  					break; 
253  				case CSSM_ALGID_ECDSA:
254  					*pemHeader = PEM_STRING_ECDSA_PUBLIC;
255  					break; 
256  				default:
257  					SecImpExpDbg("SecExportRep::exportRep unknown public key alg %lu",
258  						(unsigned long)mKeyAlg);
259  					return errSecUnsupportedFormat;
260  			}		/* end switch(mKeyAlg) */
261  			break;  /* from case externType kSecItemTypePublicKey */
262  			
263  		case kSecItemTypePrivateKey:
264  			switch(mKeyAlg) {
265  				case CSSM_ALGID_RSA:
266  					*pemHeader = PEM_STRING_RSA;
267  					break; 
268  				case CSSM_ALGID_DH:
269  					*pemHeader = PEM_STRING_DH_PRIVATE;
270  					break; 
271  				case CSSM_ALGID_DSA:
272  					*pemHeader = PEM_STRING_DSA;
273  					break; 
274  				case CSSM_ALGID_ECDSA:
275  					*pemHeader = PEM_STRING_ECDSA_PRIVATE;
276  					break; 
277  				default:
278  					SecImpExpDbg("SecExportRep::exportRep unknown private key alg "
279  						"%lu", (unsigned long)mKeyAlg);
280  					return errSecUnsupportedFormat;
281  			}		/* end switch(mKeyAlg) */
282  			break;  /* from case externType kSecItemTypePrivateKey */
283  			
284  		case kSecItemTypeSessionKey:
285  			*pemHeader = PEM_STRING_SESSION;
286  			break; 
287  		default:
288  			assert(0);
289  			return errSecInvalidItemRef;
290  	}   /* switch(mExternType) */
291  	
292  	/* Map our external params to CDSA blob format */
293  	CSSM_KEYCLASS keyClass;
294  	ortn = impExpKeyForm(format, mExternType, mKeyAlg, &blobForm, &keyClass);
295  	if(ortn) {
296  		return ortn;
297  	}
298  
299  	/* Specify format of null-wrapped key */
300  	CSSM_ATTRIBUTE_TYPE formatAttrType = CSSM_ATTRIBUTE_NONE;
301  	switch(mExternType) {
302  		case kSecItemTypePrivateKey:
303  			formatAttrType = CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT;
304  			break;
305  		case kSecItemTypePublicKey:
306  			formatAttrType = CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT;
307  			break;
308  		/* symmetric key doesn't have a format */
309  		default:
310  			break;
311  	}
312  	
313  	CSSM_CSP_HANDLE cspHand;
314  	ortn = SecKeyGetCSPHandle((SecKeyRef)mKcItem, &cspHand);
315  	if(ortn) {
316  		SecImpExpDbg("SecExportRep::exportRep SecKeyGetCSPHandle error");
317  		return ortn;
318  	}
319  
320  	/* perform the NULL wrap --> wrapped Key */
321  	CSSM_KEY wrappedKey;
322  	memset(&wrappedKey, 0, sizeof(wrappedKey));
323  	const CSSM_DATA &dd = descrData;
324  	ortn = impExpExportKeyCommon(cspHand, 
325  		(SecKeyRef)mKcItem, 
326  		NULL,							// wrappingKey not used for NULL
327  		&wrappedKey,					// destination
328  		CSSM_ALGID_NONE,				
329  		CSSM_ALGMODE_NONE, 
330  		CSSM_PADDING_NONE,
331  		CSSM_KEYBLOB_WRAPPED_FORMAT_NONE, 
332  		formatAttrType,
333  		blobForm,
334  		&dd,							// descriptiveData
335  		NULL);							// IV
336  		
337  	if(ortn == CSSM_OK) {
338  		/* pass key data back to caller */
339  		CFDataAppendBytes(outData, wrappedKey.KeyData.Data, wrappedKey.KeyData.Length);
340  	}
341  	CSSM_FreeKey(cspHand, NULL, &wrappedKey, CSSM_FALSE);
342  	return ortn;
343  }
344  
345  #pragma mark --- Certificate External rep ---
346  
347  SecExport::Cert::Cert(
348  	CFTypeRef kcItemRef) :
349  		SecExportRep(kcItemRef)
350  {
351  	mExternType = kSecItemTypeCertificate;
352  }
353  
354  SecExport::Cert::~Cert()
355  {
356  	/* nothing for now */
357  }
358  
359  /* 
360   * The heart of this class: cook up external representation, appending to 
361   * existing CFMutableDataRef.
362   */
363  OSStatus SecExport::Cert::exportRep(
364  	SecExternalFormat					format,	
365  	SecItemImportExportFlags			flags,			
366  	const SecKeyImportExportParameters	*keyParams,	// optional 
367  	CFMutableDataRef					outData,	// data appended here
368  	const char							**pemHeader)// e.g., "X509 CERTIFICATE"
369  {
370  	assert(outData != NULL);
371  	assert(mKcItem != NULL);
372  		
373  	switch(format) {
374  		case kSecFormatUnknown:		// default
375  		case kSecFormatX509Cert:	// currently, only supported format
376  			break;
377  		default:
378  			SecImpExpDbg("SecExportRep::exportRep unsupported format for cert");
379  			return errSecUnsupportedFormat;
380  	}
381  	
382  	CFDataRef cdata = SecCertificateCopyData((SecCertificateRef)mKcItem);
383  	if(!cdata) {
384  		SecImpExpDbg("SecExportRep::exportRep SecCertificateGetData error");
385  		return errSecUnsupportedFormat;
386  	}
387  
388  	CFDataAppendBytes(outData, CFDataGetBytePtr(cdata), CFDataGetLength(cdata));
389  	CFRelease(cdata);
390  	*pemHeader = PEM_STRING_X509;
391  	return errSecSuccess;
392  }
393  	
394  #pragma mark --- SecImportRep: Representation of an external object on import ---
395  
396  /* 
397   * for import, when we have the external representation.
398   * All arguments except for the CFDataRef are optional (i.e., "unknown"
399   * is legal).
400   */
401  SecImportRep::SecImportRep(
402  	CFDataRef						external,
403  	SecExternalItemType				externType,		// may be unknown 
404  	SecExternalFormat				externFormat,	// may be unknown
405  	CSSM_ALGORITHMS					keyAlg,			// may be unknown, CSSM_ALGID_NONE
406  	CFArrayRef						pemParamLines /* = NULL */ ) :
407  		mPrintName(NULL),
408  		mExternal(external),
409  		mExternType(externType),
410  		mExternFormat(externFormat),
411  		mKeyAlg(keyAlg),
412  		mPemParamLines(pemParamLines)
413  {
414  	CFRetain(mExternal);
415  }
416  
417  SecImportRep::~SecImportRep()
418  {
419  	if(mPrintName) {
420  		free(mPrintName);
421  	}
422  	if(mExternal) {
423  		CFRelease(mExternal);
424  	}
425  	if(mPemParamLines) {
426  		CFRelease(mPemParamLines);
427  	}
428  }
429  
430  /* 
431   * Convert to one or more SecItemRefs and/or import to keychain.
432   * The cspHand handle MUST be a CSPDL handle, not a raw CSP handle. 
433   */
434  OSStatus SecImportRep::importRep(
435  	SecKeychainRef						importKeychain,		// optional
436  	CSSM_CSP_HANDLE						cspHand,			// required
437  	SecItemImportExportFlags			flags,
438  	const SecKeyImportExportParameters	*keyParams,			// optional 
439  	ImpPrivKeyImportState				&keyImportState,	// IN/OUT
440  	CFMutableArrayRef					outArray)			// optional, append here 
441  {
442  	/* caller must have sorted this out by now */
443  	assert((mExternType != kSecItemTypeUnknown) &&
444  		   (mExternFormat != kSecFormatUnknown));
445  		   
446  	/* app could conceivably botch these with crafty PEM hacking */
447  	if((mExternal == NULL) || (CFDataGetLength(mExternal) == 0)) {
448  		return errSecParam;
449  	}
450  	
451  	/* handle the easy ones first */
452  	switch(mExternFormat) {
453  		case kSecFormatPKCS12:
454  			return impExpPkcs12Import(mExternal, flags, keyParams, 
455  				keyImportState, importKeychain, cspHand, outArray);
456  		case kSecFormatX509Cert:
457  		case kSecFormatPKCS7:
458  		{
459  			OSStatus rx = impExpPkcs7Import(mExternal, flags, keyParams, importKeychain, 
460  					outArray);
461  			if (rx == errSecUnknownFormat)
462  			{
463  				CSSM_DATA cdata;
464  				cdata.Data = (uint8 *)CFDataGetBytePtr(mExternal);
465  				cdata.Length = (CSSM_SIZE)CFDataGetLength(mExternal);
466  				return impExpImportCertCommon(&cdata, importKeychain, outArray);
467  			}
468  			return rx;
469  		}
470  		case kSecFormatNetscapeCertSequence:
471  			return impExpNetscapeCertImport(mExternal, flags, keyParams, importKeychain,
472  				outArray);
473  		default:
474  			break;
475  	}
476  		
477  	if((mExternType == kSecItemTypeCertificate) || 
478  	   (mExternType == kSecItemTypeAggregate)) {
479  		SecImpExpDbg("SecImportRep::importRep screwup");
480  		return errSecUnimplemented;
481  	}
482  
483  	/* 
484  	 * All that's left: keys. 
485  	 */
486  	if((mExternType == kSecItemTypePrivateKey) && (keyImportState == PIS_NoMore)) {
487  		/* multi key import against caller's wishes */
488  		return errSecMultiplePrivKeys;
489  	}
490  
491  	/* optionally infer PrintName attribute */
492  	switch(mExternFormat) {
493  		case kSecFormatSSH:
494  		case kSecFormatWrappedSSH:
495  		case kSecFormatSSHv2:
496  			mPrintName = impExpOpensshInferPrintName(mExternal, mExternType, mExternFormat);
497  			break;
498  		default:
499  			/* use defaults */
500  			break;
501  	}
502  	
503  	OSStatus ortn = errSecSuccess;
504  	
505  	switch(mExternFormat) {
506  		case kSecFormatOpenSSL:
507  		case kSecFormatSSH:	
508  		case kSecFormatSSHv2:
509  		case kSecFormatBSAFE:
510  		case kSecFormatRawKey:
511              if(mExternal != NULL || CFDataGetLength(mExternal) != 0){
512                  ortn = impExpImportRawKey(mExternal, mExternFormat, mExternType,
513                                            mKeyAlg, importKeychain, cspHand, flags, keyParams, mPrintName, outArray);
514              }
515              else{
516                  MacOSError::throwMe(errSecUnsupportedKeySize);
517              }
518              break;
519  		case kSecFormatWrappedPKCS8:
520  			ortn = impExpPkcs8Import(mExternal, importKeychain, cspHand, flags,
521  				keyParams, outArray);
522  			break;
523  		case kSecFormatWrappedOpenSSL:
524  			ortn =  importWrappedKeyOpenssl(importKeychain, cspHand, flags, keyParams, 
525  				outArray);
526  			break;
527  		case kSecFormatWrappedSSH:
528  			ortn =  impExpWrappedOpenSSHImport(mExternal, importKeychain, cspHand, 
529  				flags, keyParams, mPrintName, outArray);
530  			break;
531  		case kSecFormatWrappedLSH:
532  		default:
533  			return errSecUnknownFormat;
534  	}
535  	if((ortn == errSecSuccess) && (keyImportState == PIS_AllowOne)) {
536  		/* reached our limit */
537  		keyImportState = PIS_NoMore;
538  	}
539  	return ortn;
540  }
541