]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecImportExportCrypto.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / 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 <Security/SecImportExport.h>
29 #include "SecImportExportCrypto.h"
30 #include "SecImportExportUtils.h"
31 #include "Keychains.h"
32 #include "Access.h"
33 #include "Item.h"
34 #include <Security/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_cdsa_client/securestorage.h>
41 #include <security_cdsa_client/dlclient.h>
42 #include <Security/cssmapi.h>
43 #include <security_keychain/KeyItem.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 MacOSError::check(SecRandomCopyBytes(kSecRandomDefault, randBinaryLen, randBinary));
398
399 for(unsigned i=0; i<randBinaryLen; i++) {
400 sprintf(randAscii, "%02X", randBinary[i]);
401 randAscii += 2;
402 }
403 labelData.Data = randLabel;
404 labelData.Length = SEC_RANDOM_LABEL_LEN;
405 /* actual keyLabel value set later */
406 }
407 else {
408 labelData.Data = (uint8 *)SEC_SESSIONKEY_PRINT_NAME_ATTR_VALUE;
409 labelData.Length = strlen(SEC_SESSIONKEY_PRINT_NAME_ATTR_VALUE);
410 keyLabel.copy(labelData);
411 }
412
413 /*
414 * key attr flags and usage. First the defaults.
415 */
416 if(keyParams) {
417 keyUsage = keyParams->keyUsage;
418 keyAttributes = keyParams->keyAttributes;
419 }
420 if(keyUsage == 0) {
421 /* default */
422 keyUsage = CSSM_KEYUSE_ANY;
423 }
424 if(keyAttributes == 0) {
425 /* default */
426 keyAttributes = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE;
427 if(dlDbPtr) {
428 keyAttributes |= CSSM_KEYATTR_PERMANENT;
429 }
430 if(hdr.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) {
431 keyAttributes |= (CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE);
432 }
433 }
434 else {
435 /* caller-supplied; ensure we're generating a reference key */
436 keyAttributes &= ~SEC_KEYATTR_RETURN_MASK;
437 keyAttributes |= CSSM_KEYATTR_RETURN_REF;
438 }
439
440 if( (dlDbPtr != NULL) && // not permanent, no ACL
441 (hdr.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) && // ACLs only for private key
442 ( (keyParams == NULL) || // NULL --> default ACL
443 !(keyParams->flags & kSecKeyNoAccessControl) // explicity request no ACL
444 )
445 ) {
446 /*
447 * Prepare to set up either a default ACL or one provided by caller via
448 * keyParams->accessRef.
449 */
450 memset(&rcc, 0, sizeof(rcc));
451 maker.initialOwner(rcc);
452 rccPtr = &rcc;
453 }
454
455 /*
456 * Additional optional parameters: block size, rounds,
457 * effectiveKeySize.
458 * WARNING: block size and rounds, used for RC5, have not been tested.
459 * OpenSSL, as of Panther ship, did not support RC5 encryption.
460 */
461 if(unwrapParams->effectiveKeySizeInBits != 0) {
462 assert(unwrapParams->unwrappingKey->KeyHeader.AlgorithmId ==
463 CSSM_ALGID_RC2);
464 SecImpExpDbg("impExpImportKeyCommon: setting effectiveKeySizeInBits to %lu",
465 (unsigned long)unwrapParams->effectiveKeySizeInBits);
466 crtn = impExpAddContextAttribute(ccHand,
467 CSSM_ATTRIBUTE_EFFECTIVE_BITS,
468 sizeof(uint32),
469 (void *)((size_t) unwrapParams->effectiveKeySizeInBits));
470 if(crtn) {
471 SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error");
472 goto errOut;
473 }
474 }
475
476 if(unwrapParams->rounds != 0) {
477 assert(unwrapParams->unwrappingKey->KeyHeader.AlgorithmId ==
478 CSSM_ALGID_RC5);
479 SecImpExpDbg("impExpImportKeyCommon: setting rounds to %lu",
480 (unsigned long)unwrapParams->rounds);
481 crtn = impExpAddContextAttribute(ccHand,
482 CSSM_ATTRIBUTE_ROUNDS,
483 sizeof(uint32),
484 (void *)((size_t)unwrapParams->rounds));
485 if(crtn) {
486 SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error");
487 goto errOut;
488 }
489 }
490
491 if(unwrapParams->blockSizeInBits != 0) {
492 /* Our RC5 implementation has a fixed block size */
493 if(unwrapParams->blockSizeInBits != 64) {
494 SecImpExpDbg("WARNING impExpImportKeyCommon: setting block size to %lu",
495 (unsigned long)unwrapParams->blockSizeInBits);
496 /*
497 * With the current CSP this will actually be ignored
498 */
499 crtn = impExpAddContextAttribute(ccHand,
500 CSSM_ATTRIBUTE_BLOCK_SIZE,
501 sizeof(uint32),
502 (void *)((size_t)unwrapParams->blockSizeInBits));
503 if(crtn) {
504 SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error");
505 goto errOut;
506 }
507 }
508 }
509
510 /* Here we go */
511 crtn = CSSM_UnwrapKey(ccHand,
512 NULL, // public key
513 (const CSSM_WRAP_KEY *)wrappedKey,
514 keyUsage,
515 keyAttributes,
516 &labelData,
517 rccPtr, // CredAndAclEntry
518 &unwrappedKey,
519 &descrData); // required
520 if(crtn != CSSM_OK) {
521 SecImpExpDbg("CSSM_UnwrapKey failure");
522 if(crtn == CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA) {
523 /* report in a keychain-friendly way */
524 crtn = errSecDuplicateItem;
525 }
526 goto errOut;
527 }
528
529 /* Private and public keys: update Label as public key hash */
530 if((hdr.KeyClass != CSSM_KEYCLASS_SESSION_KEY) && (dlDbPtr != NULL)) {
531 CSSM_DATA newPrintName;
532 if(printName) {
533 /* caller specified */
534 newPrintName.Data = (uint8 *)printName;
535 }
536 else {
537 /* use defaults */
538 if(hdr.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) {
539 newPrintName.Data = (uint8 *)SEC_PRIVKEY_PRINT_NAME_ATTR_VALUE;
540 }
541 else {
542 newPrintName.Data = (uint8 *)SEC_PUBKEY_PRINT_NAME_ATTR_VALUE;
543 }
544 }
545 newPrintName.Length = strlen((char *)newPrintName.Data);
546 #if old_way
547 crtn = impExpSetKeyLabel(cspHand, *dlDbPtr, &unwrappedKey,
548 &labelData, &newPrintName, keyLabel);
549 #else
550 crtn = impExpSetKeyLabel(cspHand, *dlDbPtr, importKeychain,
551 &unwrappedKey, &labelData, &newPrintName, keyLabel, &secKeyRef);
552 #endif
553 if(crtn) {
554 goto errOut;
555 }
556 }
557
558 /* Private key: adjust ACL as appropriate */
559 if(rccPtr != NULL) {
560 SecPointer<KeychainCore::Access> theAccess(accessRef ?
561 KeychainCore::Access::required(accessRef) :
562 new KeychainCore::Access("Imported Private Key"));
563 try {
564 if(secKeyRef != NULL) {
565 // setAccess using the new secKeyRef, not the old unwrappedKey.
566 // At this point, we might have duplicate keys registered with securityd. Use the newest one.
567 theAccess->setAccess(*KeyItem::required(secKeyRef)->key(), maker);
568 } else {
569 CssmClient::KeyAclBearer bearer(cspHand, unwrappedKey, Allocator::standard());
570 theAccess->setAccess(bearer, maker);
571 }
572 }
573 catch (const CssmError &e) {
574 /* not implemented means we're talking to the raw CSP which does
575 * not implement ACLs */
576 if(e.error != CSSMERR_CSP_FUNCTION_NOT_IMPLEMENTED) {
577 crtn = e.error;
578 }
579 }
580 catch(...) {
581 SecImpExpDbg("keyImport: exception on setAccess\n");
582 crtn = errSecAuthFailed; /* ??? */
583 }
584 }
585
586 /*
587 * If importKeychain is non-NULL we've already added the key to the keychain.
588 * If importKeychain is NULL, and outArray is non-NULL, we have to use the
589 * half-baked SecKeyCreateWithCSSMKey to give the caller *something*.
590 */
591 if(outArray) {
592 if(secKeyRef == NULL) {
593 assert(importKeychain == NULL);
594 ortn = SecKeyCreateWithCSSMKey(&unwrappedKey, &secKeyRef);
595 if(ortn) {
596 SecImpExpDbg("SecKeyCreateWithCSSMKey failure");
597 crtn = ortn;
598 goto errOut;
599 }
600 /* don't CSSM_FreeKey() this key */
601 usedSecKeyCreate = true;
602 }
603 CFArrayAppendValue(outArray, secKeyRef);
604 }
605
606 if(importKeychain) {
607 impExpKeyNotify(importKeychain, keyLabel.get(), unwrappedKey);
608 }
609
610 errOut:
611 if(ccHand != 0) {
612 CSSM_DeleteContext(ccHand);
613 }
614 if(secKeyRef) {
615 CFRelease(secKeyRef);
616 }
617 if((unwrappedKey.KeyData.Data != NULL) && !usedSecKeyCreate) {
618 /* skip this free if we used SecKeyCreateWithCSSMKey() */
619 CSSM_FreeKey(cspHand, NULL, &unwrappedKey, CSSM_FALSE);
620 }
621 return crtn;
622 }
623
624 /*
625 * Common code to wrap a key for export.
626 */
627 CSSM_RETURN impExpExportKeyCommon(
628 CSSM_CSP_HANDLE cspHand, // for all three keys
629 SecKeyRef secKey,
630 CSSM_KEY_PTR wrappingKey,
631 CSSM_KEY_PTR wrappedKey, // RETURNED
632 CSSM_ALGORITHMS wrapAlg,
633 CSSM_ENCRYPT_MODE wrapMode,
634 CSSM_PADDING wrapPad,
635 CSSM_KEYBLOB_FORMAT wrapFormat, // NONE, PKCS7, PKCS8, OPENSSL
636 CSSM_ATTRIBUTE_TYPE blobAttrType, // optional raw key format attr
637 CSSM_KEYBLOB_FORMAT blobForm, // ditto
638 const CSSM_DATA *descData, // optional descriptive data
639 const CSSM_DATA *iv)
640 {
641 OSStatus ortn;
642 CSSM_RETURN crtn;
643
644 const CSSM_KEY *unwrappedKey;
645 ortn = SecKeyGetCSSMKey(secKey, &unwrappedKey);
646 if(ortn) {
647 SecImpExpDbg("impExpExportKeyCommon SecKeyGetCSSMKey error");
648 return ortn;
649 }
650 else if(!(unwrappedKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE)) {
651 SecImpExpDbg("impExpExportKeyCommon: CSSM key is non-extractable");
652 return errSecDataNotAvailable;
653 }
654
655 /*
656 * Creds are needed for wrapping private and session keys.
657 */
658 CSSM_ACCESS_CREDENTIALS nullCreds;
659 memset(&nullCreds, 0, sizeof(nullCreds));
660 const CSSM_ACCESS_CREDENTIALS *creds = &nullCreds; // default
661
662 CSSM_KEYCLASS keyClass = unwrappedKey->KeyHeader.KeyClass;
663 if(keyClass == CSSM_KEYCLASS_PRIVATE_KEY || keyClass == CSSM_KEYCLASS_SESSION_KEY) {
664 ortn = SecKeyGetCredentials(secKey,
665 CSSM_ACL_AUTHORIZATION_DECRYPT,
666 kSecCredentialTypeDefault,
667 &creds);
668 if(ortn) {
669 SecImpExpDbg("impExpExportKeyCommon SecKeyGetCredentials error");
670 return ortn;
671 }
672 }
673
674 CSSM_CC_HANDLE ccHand;
675 crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
676 wrapAlg,
677 wrapMode,
678 &nullCreds, // creds for wrapping key, never a private key here
679 wrappingKey,
680 iv,
681 wrapPad,
682 0, // Params
683 &ccHand);
684 if(ortn) {
685 SecImpExpDbg("impExpExportKeyCommon CSSM_CSP_CreateSymmetricContext error");
686 return crtn;
687 }
688
689 /* a couple of optional caller-specified attributes */
690 if(wrapFormat != CSSM_KEYBLOB_WRAPPED_FORMAT_NONE) {
691 crtn = impExpAddContextAttribute(ccHand,
692 CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT,
693 sizeof(uint32),
694 (void *)((size_t)wrapFormat));
695 if(crtn) {
696 SecImpExpDbg("impExpExportKeyCommon AddContextAttribute error (1)");
697 CSSM_DeleteContext(ccHand);
698 return crtn;
699 }
700 }
701
702 if(blobAttrType != CSSM_ATTRIBUTE_NONE) {
703 crtn = impExpAddContextAttribute(ccHand,
704 blobAttrType,
705 sizeof(uint32),
706 (void *)((size_t)blobForm));
707 if(crtn) {
708 SecImpExpDbg("impExpExportKeyCommon AddContextAttribute error");
709 return crtn;
710 }
711 }
712
713 CSSM_DATA dData = {0, 0};
714 if(descData) {
715 dData = *descData;
716 }
717
718 crtn = CSSM_WrapKey(ccHand,
719 creds,
720 unwrappedKey,
721 &dData,
722 wrappedKey);
723 CSSM_DeleteContext(ccHand);
724 switch(crtn) {
725 case CSSM_OK:
726 break;
727 case CSSMERR_CSP_INVALID_KEYATTR_MASK:
728 {
729 /*
730 * This is what comes back when we try to wrap an unextractable
731 * key, or when we null wrap a sensitive key. Give the caller
732 * some useful info.
733 */
734 CSSM_KEYATTR_FLAGS attr = unwrappedKey->KeyHeader.KeyAttr;
735 if(!(attr & CSSM_KEYATTR_EXTRACTABLE)) {
736 SecImpExpDbg("impExpExportKeyCommon !EXTRACTABLE");
737 return errSecDataNotAvailable;
738 }
739 if((attr & CSSM_KEYATTR_SENSITIVE) && (wrappingKey == NULL)) {
740 SecImpExpDbg("impExpExportKeyCommon !SENSITIVE, NULL wrap");
741 return errSecPassphraseRequired;
742 }
743
744 }
745 default:
746 SecImpExpDbg("impExpExportKeyCommon CSSM_WrapKey error");
747 }
748 return crtn;
749 }