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