]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/Security/SecImportExportCrypto.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / Security / SecImportExportCrypto.cpp
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 */
71 static 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;
153 errOut:
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 */
169 OSStatus 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
245 using 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 */
252 OSStatus 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 */
321 OSStatus 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
605 errOut:
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 */
622 CSSM_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 }