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