1/*
2 * Copyright (c) 2004 Apple Computer, 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
40using namespace Security;
41using namespace KeychainCore;
42
43
44#pragma mark --- SecExportRep Subclasses seen only by SecExportRep::vend() ---
45
46namespace SecExport {
47
48class Key : public SecExportRep
49{
50	friend class SecExportRep;
51protected:
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
62private:
63	CSSM_ALGORITHMS						mKeyAlg;
64	const CSSM_KEY						*mCssmKey;
65};
66
67class Cert : public SecExportRep
68{
69	friend class SecExportRep;
70protected:
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
86SecExportRep::SecExportRep(
87	CFTypeRef			kcItemRef) :
88		mKcItem((SecKeychainItemRef)kcItemRef),
89		mPemParamLines(NULL)
90{
91	CFRetain(mKcItem);
92}
93
94SecExportRep::~SecExportRep()
95{
96	if(mKcItem) {
97		CFRelease(mKcItem);
98	}
99	if(mPemParamLines) {
100		CFRelease(mPemParamLines);
101	}
102}
103
104SecExportRep::SecExportRep() {
105	MacOSError::throwMe(errSecInvalidItemRef);
106}
107
108/* must be implemented by subclass */
109OSStatus 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 */
128SecExportRep *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
145SecExport::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
178SecExport::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 */
187OSStatus 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
347SecExport::Cert::Cert(
348	CFTypeRef kcItemRef) :
349		SecExportRep(kcItemRef)
350{
351	mExternType = kSecItemTypeCertificate;
352}
353
354SecExport::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 */
363OSStatus 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 */
401SecImportRep::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
417SecImportRep::~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 */
434OSStatus 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			ortn = impExpImportRawKey(mExternal, mExternFormat, mExternType,
512				mKeyAlg, importKeychain, cspHand, flags, keyParams, mPrintName, outArray);
513			break;
514		case kSecFormatWrappedPKCS8:
515			ortn = impExpPkcs8Import(mExternal, importKeychain, cspHand, flags,
516				keyParams, outArray);
517			break;
518		case kSecFormatWrappedOpenSSL:
519			ortn =  importWrappedKeyOpenssl(importKeychain, cspHand, flags, keyParams,
520				outArray);
521			break;
522		case kSecFormatWrappedSSH:
523			ortn =  impExpWrappedOpenSSHImport(mExternal, importKeychain, cspHand,
524				flags, keyParams, mPrintName, outArray);
525			break;
526		case kSecFormatWrappedLSH:
527		default:
528			return errSecUnknownFormat;
529	}
530	if((ortn == errSecSuccess) && (keyImportState == PIS_AllowOne)) {
531		/* reached our limit */
532		keyImportState = PIS_NoMore;
533	}
534	return ortn;
535}
536
537