1/*
2 * Copyright (c) 2000-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/*
24 * SecImportExportCrypto.cpp - low-level crypto routines for wrapping and unwrapping
25 *							   keys.
26 */
27
28#include "SecImportExport.h"
29#include "SecImportExportCrypto.h"
30#include "SecImportExportUtils.h"
31#include "Keychains.h"
32#include "Access.h"
33#include "Item.h"
34#include "SecKeyPriv.h"
35#include "KCEventNotifier.h"
36#include <security_cdsa_utilities/cssmacl.h>
37#include <security_cdsa_utilities/KeySchema.h>
38#include <security_cdsa_utilities/cssmdata.h>
39#include <security_cdsa_utils/cuCdsaUtils.h>
40#include <security_utilities/devrandom.h>
41#include <security_cdsa_client/securestorage.h>
42#include <security_cdsa_client/dlclient.h>
43#include <Security/cssmapi.h>
44
45/*
46 * Key attrribute names and values.
47 *
48 * This is where the public key hash goes.
49 */
50#define SEC_KEY_HASH_ATTR_NAME			"Label"
51
52/*
53 * This is where the publicly visible name goes.
54 */
55#define SEC_KEY_PRINT_NAME_ATTR_NAME	"PrintName"
56
57/*
58 * Default values we ultimately assign to the PrintName attr.
59 */
60#define SEC_PRIVKEY_PRINT_NAME_ATTR_VALUE		"Imported Private Key"
61#define SEC_PUBKEY_PRINT_NAME_ATTR_VALUE		"Imported Public Key"
62#define SEC_SESSIONKEY_PRINT_NAME_ATTR_VALUE	"Imported Key"
63
64/*
65 * Set private key's Label and PrintName attributes. On entry Label
66 * is typically a random string to faciliate finding the key in a DL;
67 * the PrintName is currently set to the same value by the DL. We
68 * replace the Label attr with the public key hash and the PrintName
69 * attr with a caller-supplied value.
70 */
71static CSSM_RETURN impExpSetKeyLabel(
72	CSSM_CSP_HANDLE 	cspHand,		// where the key lives
73	CSSM_DL_DB_HANDLE 	dlDbHand,		// ditto
74	SecKeychainRef		kcRef,			// ditto
75	CSSM_KEY_PTR		cssmKey,
76	const CSSM_DATA		*existKeyLabel,	// existing label, a random string
77	const CSSM_DATA		*newPrintName,
78	CssmOwnedData		&newLabel,		// RETURNED as what we set
79	SecKeyRef			*secKey)		// RETURNED
80{
81	CSSM_RETURN						crtn;
82	CSSM_DATA						keyDigest = {0, NULL};
83
84	crtn = impExpKeyDigest(cspHand, cssmKey, &keyDigest);
85	if(crtn) {
86		return crtn;
87	}
88
89	/* caller needs this for subsequent DL lookup */
90	newLabel.copy(keyDigest);
91
92	/* Find this key as a SecKeychainItem */
93	SecItemClass itemClass = (cssmKey->KeyHeader.KeyClass ==
94		CSSM_KEYCLASS_PRIVATE_KEY) ? kSecPrivateKeyItemClass :
95			kSecPublicKeyItemClass;
96	SecKeychainAttribute kcAttr = {kSecKeyLabel, (UInt32)existKeyLabel->Length, existKeyLabel->Data};
97	SecKeychainAttributeList kcAttrList = {1, &kcAttr};
98	SecKeychainSearchRef srchRef = NULL;
99	OSStatus ortn;
100	SecKeychainItemRef itemRef = NULL;
101
102	ortn = SecKeychainSearchCreateFromAttributes(kcRef, itemClass,
103		&kcAttrList, &srchRef);
104	if(ortn) {
105		SecImpExpDbg("SecKeychainSearchCreateFromAttributes error");
106		crtn = ortn;
107		goto errOut;
108	}
109	ortn = SecKeychainSearchCopyNext(srchRef, &itemRef);
110	if(ortn) {
111		SecImpExpDbg("SecKeychainSearchCopyNext error");
112		crtn = ortn;
113		goto errOut;
114	}
115	#ifndef	NDEBUG
116	ortn = SecKeychainSearchCopyNext(srchRef, &itemRef);
117	if(ortn == errSecSuccess) {
118		SecImpExpDbg("impExpSetKeyLabel: found second key with same label!");
119		crtn = errSecInternalComponent;
120		goto errOut;
121	}
122	#endif	/* NDEBUG */
123
124	/* modify two attributes... */
125	SecKeychainAttribute modAttrs[2];
126	modAttrs[0].tag    = kSecKeyLabel;
127	modAttrs[0].length = (UInt32)keyDigest.Length;
128	modAttrs[0].data   = keyDigest.Data;
129	modAttrs[1].tag    = kSecKeyPrintName;
130	modAttrs[1].length = (UInt32)newPrintName->Length;
131	modAttrs[1].data   = newPrintName->Data;
132	kcAttrList.count = 2;
133	kcAttrList.attr = modAttrs;
134	ortn = SecKeychainItemModifyAttributesAndData(itemRef, &kcAttrList,
135		0, NULL);
136	if(ortn) {
137		SecImpExpDbg("SecKeychainItemModifyAttributesAndData error");
138		crtn = ortn;
139		goto errOut;
140	}
141	*secKey = (SecKeyRef)itemRef;
142errOut:
143	if(keyDigest.Data)  {
144		/* mallocd by CSP */
145		impExpFreeCssmMemory(cspHand, keyDigest.Data);
146	}
147	if(srchRef) {
148		CFRelease(srchRef);
149	}
150	return crtn;
151}
152
153/*
154 * Import a raw key. This can be used as a lightweight "guess" evaluator
155 * if a handle to the raw CSP is passed in (with no keychain), or as
156 * the real thing which does full keychain import.
157 */
158OSStatus impExpImportRawKey(
159	CFDataRef						inData,
160	SecExternalFormat				externForm,
161	SecExternalItemType				itemType,
162	CSSM_ALGORITHMS					keyAlg,
163	SecKeychainRef					importKeychain, // optional
164	CSSM_CSP_HANDLE					cspHand,		// required
165	SecItemImportExportFlags		flags,
166	const SecKeyImportExportParameters	*keyParams,		// optional
167	const char						*printName,		// optional
168	CFMutableArrayRef				outArray)		// optional, append here
169{
170	CSSM_RETURN			crtn;
171	CSSM_KEY			wrappedKey;
172	CSSM_KEYHEADER		&hdr = wrappedKey.KeyHeader;
173	CSSM_CSP_HANDLE		rawCspHand = 0;
174	CSSM_KEY_SIZE		keySize;
175	CSSM_KEYBLOB_FORMAT format;
176	CSSM_KEYCLASS		keyClass;
177
178	/* First convert external format and types to CSSM style. */
179	crtn = impExpKeyForm(externForm, itemType, keyAlg, &format, &keyClass);
180
181	/* cook up key to be null-unwrapped */
182	memset(&wrappedKey, 0, sizeof(CSSM_KEY));
183	wrappedKey.KeyData.Length = CFDataGetLength(inData);
184	wrappedKey.KeyData.Data   = (uint8 *)CFDataGetBytePtr(inData);
185
186	hdr.HeaderVersion = CSSM_KEYHEADER_VERSION;
187	/* CspId don't care */
188	hdr.BlobType = CSSM_KEYBLOB_RAW;
189	hdr.Format = format;
190	hdr.AlgorithmId = keyAlg;
191	hdr.KeyClass = keyClass;
192	/* LogicalKeySizeInBits calculated below */
193	/* attr and usage are for the incoming unwrapped key... */
194	hdr.KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
195	hdr.KeyUsage = CSSM_KEYUSE_ANY;
196
197	/*
198	 * Get key size in bits from raw CSP. Doing this right now is a good
199	 * optimization for the "guessing" case; getting the key size from the
200	 * raw CSP involves a full decode on an alg- and format-specific manner.
201	 * If we've been given the wrong params, we'll fail right here without
202	 * the complication of a full UnwrapKey op.
203	 */
204	rawCspHand = cuCspStartup(CSSM_TRUE);
205	if(rawCspHand == 0) {
206		return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
207	}
208	crtn = CSSM_QueryKeySizeInBits(rawCspHand, CSSM_INVALID_HANDLE, &wrappedKey, &keySize);
209	cuCspDetachUnload(rawCspHand, CSSM_TRUE);
210	if(crtn) {
211		SecImpExpDbg("CSSM_QueryKeySizeInBits error");
212		return crtn;
213	}
214	hdr.LogicalKeySizeInBits = keySize.LogicalKeySizeInBits;
215
216	impExpKeyUnwrapParams unwrapParams;
217	memset(&unwrapParams, 0, sizeof(unwrapParams));
218	unwrapParams.encrAlg		= CSSM_ALGID_NONE;
219	unwrapParams.encrMode		= CSSM_ALGMODE_NONE;
220	unwrapParams.unwrappingKey  = NULL;
221	unwrapParams.encrPad		= CSSM_PADDING_NONE;
222
223	return impExpImportKeyCommon(
224		&wrappedKey,
225		importKeychain,
226		cspHand,
227		flags,
228		keyParams,
229		&unwrapParams,
230		printName,
231		outArray);
232}
233
234using namespace KeychainCore;
235
236/*
237 * Post notification of a "new key added" event.
238 * If you know of another way to do this, other than a dlclient-based lookup of the
239 * existing key in order to get a KeychainCore::Item, by all means have at it.
240 */
241OSStatus impExpKeyNotify(
242	SecKeychainRef	importKeychain,
243	const CssmData	&keyLabel,		// stored with this, we use it to do a lookup
244	const CSSM_KEY	&cssmKey)		// unwrapped key in CSSM format
245{
246	/*
247	 * Look up key in the DLDB by label, key class, algorithm, and key size.
248	 */
249	CSSM_DB_RECORDTYPE recordType;
250	const CSSM_KEYHEADER &hdr = cssmKey.KeyHeader;
251
252	switch(hdr.KeyClass) {
253		case CSSM_KEYCLASS_PUBLIC_KEY:
254			recordType = CSSM_DL_DB_RECORD_PUBLIC_KEY;
255			break;
256		case CSSM_KEYCLASS_PRIVATE_KEY:
257			recordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
258			break;
259		case CSSM_KEYCLASS_SESSION_KEY:
260			recordType = CSSM_DL_DB_RECORD_SYMMETRIC_KEY;
261			break;
262		default:
263			return errSecParam;
264	}
265	assert(importKeychain != NULL);
266	Keychain keychain = KeychainImpl::required(importKeychain);
267
268	SSDbImpl* impl = dynamic_cast<CssmClient::SSDbImpl *>(&(*keychain->database()));
269	if (impl == NULL) // did we go bad?
270	{
271		CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
272	}
273
274	CssmClient::SSDb ssDb(impl);
275
276	CssmClient::DbAttributes dbAttributes;
277	CssmClient::DbUniqueRecord uniqueId;
278	CssmClient::SSDbCursor dbCursor(ssDb, 3);		// three attributes
279	dbCursor->recordType(recordType);
280	dbCursor->add(CSSM_DB_EQUAL, KeySchema::Label, keyLabel);
281	dbCursor->add(CSSM_DB_EQUAL, KeySchema::KeyType, hdr.AlgorithmId);
282	dbCursor->add(CSSM_DB_EQUAL, KeySchema::KeySizeInBits, hdr.LogicalKeySizeInBits);
283	CssmClient::Key key;
284	if (!dbCursor->nextKey(&dbAttributes, key, uniqueId)) {
285		SecImpExpDbg("impExpKeyNotify: key not found");
286		return errSecItemNotFound;
287	}
288
289	/*
290	 * Get a Keychain-style Item, post notification.
291	 */
292	Item keyItem = keychain->item(recordType, uniqueId);
293	keychain->postEvent(kSecAddEvent, keyItem);
294
295	return errSecSuccess;
296}
297
298/*
299 * Size of random label string in ASCII chars to facilitate DL lookup.
300 */
301#define SEC_RANDOM_LABEL_LEN		16
302
303#define SEC_KEYATTR_RETURN_MASK		\
304	(CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_RETURN_NONE)
305
306/*
307 * Common code to unwrap a key, used for raw keys (which do a NULL unwrap) and
308 * wrapped keys.
309 */
310OSStatus impExpImportKeyCommon(
311	const CSSM_KEY					*wrappedKey,
312	SecKeychainRef					importKeychain, // optional
313	CSSM_CSP_HANDLE					cspHand,		// required, if importKeychain is
314													// present, must be from there
315	SecItemImportExportFlags		flags,
316	const SecKeyImportExportParameters	*keyParams, // optional
317	const impExpKeyUnwrapParams		*unwrapParams,
318	const char						*printName,		// optional
319	CFMutableArrayRef				outArray)		// optional, append here
320{
321	CSSM_CC_HANDLE		ccHand = 0;
322	CSSM_RETURN			crtn;
323	CSSM_DATA			labelData;
324	CSSM_KEY			unwrappedKey;
325	CSSM_DL_DB_HANDLE   dlDbHandle;
326	CSSM_DL_DB_HANDLE   *dlDbPtr = NULL;
327	OSStatus			ortn;
328	CSSM_ACCESS_CREDENTIALS	nullCreds;
329	uint8				randLabel[SEC_RANDOM_LABEL_LEN + 1];
330	CSSM_KEYUSE			keyUsage = 0;			// default
331	CSSM_KEYATTR_FLAGS  keyAttributes = 0;		// default
332	const CSSM_KEYHEADER &hdr = wrappedKey->KeyHeader;
333	CSSM_DATA			descrData = {0, NULL};
334	ResourceControlContext rcc;
335	Security::KeychainCore::Access::Maker maker;
336	ResourceControlContext *rccPtr = NULL;
337	SecAccessRef		accessRef = keyParams ? keyParams->accessRef : NULL;
338	CssmAutoData		keyLabel(Allocator::standard());
339	SecKeyRef			secKeyRef = NULL;
340	bool				usedSecKeyCreate = false;
341
342	assert(unwrapParams != NULL);
343	assert(cspHand != 0);
344
345	if(importKeychain) {
346		ortn = SecKeychainGetDLDBHandle(importKeychain, &dlDbHandle);
347		if(ortn) {
348			return ortn;
349		}
350		dlDbPtr = &dlDbHandle;
351	}
352
353	memset(&unwrappedKey, 0, sizeof(CSSM_KEY));
354	memset(&nullCreds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
355
356	/* context for unwrap */
357	crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
358			unwrapParams->encrAlg,
359			unwrapParams->encrMode,
360			&nullCreds,
361			unwrapParams->unwrappingKey,
362			unwrapParams->iv.Data ? &unwrapParams->iv : NULL,
363			unwrapParams->encrPad,
364			0,				// Params
365			&ccHand);
366	if(crtn) {
367		goto errOut;
368	}
369	if(dlDbPtr) {
370		/* Importing to a keychain - add DLDB to context */
371		crtn = impExpAddContextAttribute(ccHand,
372			CSSM_ATTRIBUTE_DL_DB_HANDLE,
373			sizeof(CSSM_ATTRIBUTE_DL_DB_HANDLE),
374			dlDbPtr);
375		if(crtn) {
376			SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error");
377			goto errOut;
378		}
379	}
380
381	if((hdr.KeyClass != CSSM_KEYCLASS_SESSION_KEY) && (dlDbPtr != NULL)) {
382		/* Generate random 16-char label to facilitate DL lookup */
383		char *randAscii = (char *)randLabel;
384		uint8 randBinary[SEC_RANDOM_LABEL_LEN / 2];
385		unsigned randBinaryLen = SEC_RANDOM_LABEL_LEN / 2;
386		DevRandomGenerator rng;
387
388		rng.random(randBinary, randBinaryLen);
389		for(unsigned i=0; i<randBinaryLen; i++) {
390			sprintf(randAscii, "%02X", randBinary[i]);
391			randAscii += 2;
392		}
393		labelData.Data = randLabel;
394		labelData.Length = SEC_RANDOM_LABEL_LEN;
395		/* actual keyLabel value set later */
396	}
397	else {
398		labelData.Data = (uint8 *)SEC_SESSIONKEY_PRINT_NAME_ATTR_VALUE;
399		labelData.Length = strlen(SEC_SESSIONKEY_PRINT_NAME_ATTR_VALUE);
400		keyLabel.copy(labelData);
401	}
402
403	/*
404	 * key attr flags and usage. First the defaults.
405	 */
406	if(keyParams) {
407		keyUsage = keyParams->keyUsage;
408		keyAttributes = keyParams->keyAttributes;
409	}
410	if(keyUsage == 0) {
411		/* default */
412		keyUsage = CSSM_KEYUSE_ANY;
413	}
414	if(keyAttributes == 0) {
415		/* default */
416		keyAttributes = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE;
417		if(dlDbPtr) {
418			keyAttributes |= CSSM_KEYATTR_PERMANENT;
419		}
420		if(hdr.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) {
421			keyAttributes |= (CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE);
422		}
423	}
424	else {
425		/* caller-supplied; ensure we're generating a reference key */
426		keyAttributes &= ~SEC_KEYATTR_RETURN_MASK;
427		keyAttributes |= CSSM_KEYATTR_RETURN_REF;
428	}
429
430	if( (dlDbPtr != NULL) &&							// not permanent, no ACL
431	    (hdr.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) &&	// ACLs only for private key
432		( (keyParams == NULL) ||						// NULL --> default ACL
433		  !(keyParams->flags & kSecKeyNoAccessControl)  // explicity request no ACL
434		)
435	  ) {
436		/*
437		 * Prepare to set up either a default ACL or one provided by caller via
438		 * keyParams->accessRef.
439		 */
440		memset(&rcc, 0, sizeof(rcc));
441		maker.initialOwner(rcc);
442		rccPtr = &rcc;
443	}
444
445	/*
446	 * Additional optional parameters: block size, rounds,
447	 * effectiveKeySize.
448	 * WARNING: block size and rounds, used for RC5, have not been tested.
449	 * OpenSSL, as of Panther ship, did not support RC5 encryption.
450	 */
451	if(unwrapParams->effectiveKeySizeInBits != 0) {
452		assert(unwrapParams->unwrappingKey->KeyHeader.AlgorithmId ==
453			CSSM_ALGID_RC2);
454		SecImpExpDbg("impExpImportKeyCommon: setting effectiveKeySizeInBits to %lu",
455			(unsigned long)unwrapParams->effectiveKeySizeInBits);
456		crtn = impExpAddContextAttribute(ccHand,
457			CSSM_ATTRIBUTE_EFFECTIVE_BITS,
458			sizeof(uint32),
459			(void *)((size_t) unwrapParams->effectiveKeySizeInBits));
460		if(crtn) {
461			SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error");
462			goto errOut;
463		}
464	}
465
466	if(unwrapParams->rounds != 0) {
467		assert(unwrapParams->unwrappingKey->KeyHeader.AlgorithmId ==
468			CSSM_ALGID_RC5);
469		SecImpExpDbg("impExpImportKeyCommon: setting rounds to %lu",
470			(unsigned long)unwrapParams->rounds);
471		crtn = impExpAddContextAttribute(ccHand,
472			CSSM_ATTRIBUTE_ROUNDS,
473			sizeof(uint32),
474			(void *)((size_t)unwrapParams->rounds));
475		if(crtn) {
476			SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error");
477			goto errOut;
478		}
479	}
480
481	if(unwrapParams->blockSizeInBits != 0) {
482		/* Our RC5 implementation has a fixed block size */
483		if(unwrapParams->blockSizeInBits != 64) {
484			SecImpExpDbg("WARNING impExpImportKeyCommon: setting block size to %lu",
485				(unsigned long)unwrapParams->blockSizeInBits);
486			/*
487			 * With the current CSP this will actually be ignored
488			 */
489			crtn = impExpAddContextAttribute(ccHand,
490				CSSM_ATTRIBUTE_BLOCK_SIZE,
491				sizeof(uint32),
492				(void *)((size_t)unwrapParams->blockSizeInBits));
493			if(crtn) {
494				SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error");
495				goto errOut;
496			}
497		}
498	}
499
500	/* Here we go */
501	crtn = CSSM_UnwrapKey(ccHand,
502		NULL,				// public key
503		(const CSSM_WRAP_KEY *)wrappedKey,
504		keyUsage,
505		keyAttributes,
506		&labelData,
507		rccPtr,				// CredAndAclEntry
508		&unwrappedKey,
509		&descrData);		// required
510	if(crtn != CSSM_OK) {
511		SecImpExpDbg("CSSM_UnwrapKey failure");
512		if(crtn == CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA) {
513			/* report in a keychain-friendly way */
514			crtn = errSecDuplicateItem;
515		}
516		goto errOut;
517	}
518
519	/* Private and public keys: update Label as public key hash */
520	if((hdr.KeyClass != CSSM_KEYCLASS_SESSION_KEY) && (dlDbPtr != NULL)) {
521		CSSM_DATA newPrintName;
522		if(printName) {
523			/* caller specified */
524			newPrintName.Data = (uint8 *)printName;
525		}
526		else {
527			/* use defaults */
528			if(hdr.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) {
529				newPrintName.Data = (uint8 *)SEC_PRIVKEY_PRINT_NAME_ATTR_VALUE;
530			}
531			else {
532				newPrintName.Data = (uint8 *)SEC_PUBKEY_PRINT_NAME_ATTR_VALUE;
533			}
534		}
535		newPrintName.Length = strlen((char *)newPrintName.Data);
536		#if old_way
537		crtn = impExpSetKeyLabel(cspHand, *dlDbPtr, &unwrappedKey,
538			&labelData, &newPrintName, keyLabel);
539		#else
540		crtn = impExpSetKeyLabel(cspHand, *dlDbPtr, importKeychain,
541			&unwrappedKey, &labelData, &newPrintName, keyLabel, &secKeyRef);
542		#endif
543		if(crtn) {
544			goto errOut;
545		}
546	}
547
548	/* Private key: adjust ACL as appropriate */
549	if(rccPtr != NULL) {
550		SecPointer<KeychainCore::Access> theAccess(accessRef ?
551			KeychainCore::Access::required(accessRef) :
552			new KeychainCore::Access("Imported Private Key"));
553		try {
554			CssmClient::KeyAclBearer bearer(cspHand, unwrappedKey, Allocator::standard());
555			theAccess->setAccess(bearer, maker);
556		}
557		catch (const CssmError &e) {
558			/* not implemented means we're talking to the raw CSP which does
559			 * not implement ACLs */
560			if(e.error != CSSMERR_CSP_FUNCTION_NOT_IMPLEMENTED) {
561				crtn = e.error;
562			}
563		}
564		catch(...) {
565			SecImpExpDbg("keyImport: exception on setAccess\n");
566			crtn = errSecAuthFailed;	/* ??? */
567		}
568	}
569
570	/*
571	 * If importKeychain is non-NULL we've already added the key to the keychain.
572	 * If importKeychain is NULL, and outArray is non-NULL, we have to use the
573	 * half-baked SecKeyCreateWithCSSMKey to give the caller *something*.
574	 */
575	if(outArray) {
576		if(secKeyRef == NULL) {
577			assert(importKeychain == NULL);
578			ortn = SecKeyCreateWithCSSMKey(&unwrappedKey, &secKeyRef);
579			if(ortn) {
580				SecImpExpDbg("SecKeyCreateWithCSSMKey failure");
581				crtn = ortn;
582				goto errOut;
583			}
584			/* don't CSSM_FreeKey() this key */
585			usedSecKeyCreate = true;
586		}
587		CFArrayAppendValue(outArray, secKeyRef);
588	}
589
590	if(importKeychain) {
591		impExpKeyNotify(importKeychain, keyLabel.get(), unwrappedKey);
592	}
593
594errOut:
595	if(ccHand != 0) {
596		CSSM_DeleteContext(ccHand);
597	}
598	if(secKeyRef) {
599		CFRelease(secKeyRef);
600	}
601	if((unwrappedKey.KeyData.Data != NULL) && !usedSecKeyCreate) {
602		/* skip this free if we used SecKeyCreateWithCSSMKey() */
603		CSSM_FreeKey(cspHand, NULL, &unwrappedKey, CSSM_FALSE);
604	}
605	return crtn;
606}
607
608/*
609 * Common code to wrap a key for export.
610 */
611CSSM_RETURN impExpExportKeyCommon(
612	CSSM_CSP_HANDLE		cspHand,		// for all three keys
613	SecKeyRef			secKey,
614	CSSM_KEY_PTR		wrappingKey,
615	CSSM_KEY_PTR		wrappedKey,		// RETURNED
616	CSSM_ALGORITHMS		wrapAlg,
617	CSSM_ENCRYPT_MODE   wrapMode,
618	CSSM_PADDING		wrapPad,
619	CSSM_KEYBLOB_FORMAT	wrapFormat,		// NONE, PKCS7, PKCS8, OPENSSL
620	CSSM_ATTRIBUTE_TYPE blobAttrType,	// optional raw key format attr
621	CSSM_KEYBLOB_FORMAT blobForm,		// ditto
622	const CSSM_DATA		*descData,		// optional descriptive data
623	const CSSM_DATA		*iv)
624{
625	OSStatus ortn;
626	CSSM_RETURN crtn;
627
628	const CSSM_KEY *unwrappedKey;
629	ortn = SecKeyGetCSSMKey(secKey, &unwrappedKey);
630	if(ortn) {
631		SecImpExpDbg("impExpExportKeyCommon SecKeyGetCSSMKey error");
632		return ortn;
633	}
634	else if(!(unwrappedKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE)) {
635		SecImpExpDbg("impExpExportKeyCommon: CSSM key is non-extractable");
636		return errSecDataNotAvailable;
637	}
638
639	/*
640	 * Creds needed only for wrapping private keys.
641	 * We bother checking in case we ever want to use this to wrap
642	 * session keys.
643	 */
644	CSSM_ACCESS_CREDENTIALS	nullCreds;
645	memset(&nullCreds, 0, sizeof(nullCreds));
646	const CSSM_ACCESS_CREDENTIALS *creds = &nullCreds;		// default
647
648	CSSM_KEYCLASS keyClass = unwrappedKey->KeyHeader.KeyClass;
649	if(keyClass == CSSM_KEYCLASS_PRIVATE_KEY) {
650		ortn = SecKeyGetCredentials(secKey,
651			CSSM_ACL_AUTHORIZATION_DECRYPT,			// HACK will change!
652			kSecCredentialTypeDefault,
653			&creds);
654		if(ortn) {
655			SecImpExpDbg("impExpExportKeyCommon SecKeyGetCredentials error");
656			return ortn;
657		}
658	}
659
660	CSSM_CC_HANDLE ccHand;
661	crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
662		wrapAlg,
663		wrapMode,
664		&nullCreds,			// creds for wrapping key, never a private key here
665		wrappingKey,
666		iv,
667		wrapPad,
668		0,					// Params
669		&ccHand);
670	if(ortn) {
671		SecImpExpDbg("impExpExportKeyCommon CSSM_CSP_CreateSymmetricContext error");
672		return crtn;
673	}
674
675	/* a couple of optional caller-specified attributes */
676	if(wrapFormat != CSSM_KEYBLOB_WRAPPED_FORMAT_NONE) {
677		crtn = impExpAddContextAttribute(ccHand,
678			CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT,
679			sizeof(uint32),
680			(void *)((size_t)wrapFormat));
681		if(crtn) {
682			SecImpExpDbg("impExpExportKeyCommon AddContextAttribute error (1)");
683			CSSM_DeleteContext(ccHand);
684			return crtn;
685		}
686	}
687
688	if(blobAttrType != CSSM_ATTRIBUTE_NONE) {
689		crtn = impExpAddContextAttribute(ccHand,
690			blobAttrType,
691			sizeof(uint32),
692			(void *)((size_t)blobForm));
693		if(crtn) {
694			SecImpExpDbg("impExpExportKeyCommon AddContextAttribute error");
695			return crtn;
696		}
697	}
698
699	CSSM_DATA dData = {0, 0};
700	if(descData) {
701		dData = *descData;
702	}
703
704	crtn = CSSM_WrapKey(ccHand,
705		creds,
706		unwrappedKey,
707		&dData,
708		wrappedKey);
709	CSSM_DeleteContext(ccHand);
710	switch(crtn) {
711		case CSSM_OK:
712			break;
713		case CSSMERR_CSP_INVALID_KEYATTR_MASK:
714		{
715			/*
716			 * This is what comes back when we try to wrap an unextractable
717			 * key, or when we null wrap a sensitive key. Give the caller
718			 * some useful info.
719			 */
720			CSSM_KEYATTR_FLAGS attr = unwrappedKey->KeyHeader.KeyAttr;
721			if(!(attr & CSSM_KEYATTR_EXTRACTABLE)) {
722				SecImpExpDbg("impExpExportKeyCommon !EXTRACTABLE");
723				return errSecDataNotAvailable;
724			}
725			if((attr & CSSM_KEYATTR_SENSITIVE) && (wrappingKey == NULL)) {
726				SecImpExpDbg("impExpExportKeyCommon !SENSITIVE, NULL wrap");
727				return errSecPassphraseRequired;
728			}
729
730		}
731		default:
732			SecImpExpDbg("impExpExportKeyCommon CSSM_WrapKey error");
733	}
734	return crtn;
735}
736