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 }