SecImportExportUtils.cpp
1 /* 2 * Copyright (c) 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 * SecImportExportUtils.cpp - misc. utilities for import/export module 24 */ 25 26 #include "SecImportExportUtils.h" 27 #include "SecImportExportAgg.h" 28 #include "SecImportExportCrypto.h" 29 #include <Security/SecIdentityPriv.h> 30 #include <Security/SecItem.h> 31 #include <security_cdsa_utils/cuCdsaUtils.h> 32 #include <security_utilities/casts.h> 33 #include <Security/SecBase.h> 34 #pragma mark --- Debug support --- 35 36 #ifndef NDEBUG 37 38 const char *impExpExtFormatStr( 39 SecExternalFormat format) 40 { 41 switch(format) { 42 case kSecFormatUnknown: return "kSecFormatUnknown"; 43 case kSecFormatOpenSSL: return "kSecFormatOpenSSL"; 44 case kSecFormatSSH: return "kSecFormatSSH"; 45 case kSecFormatBSAFE: return "kSecFormatBSAFE"; 46 case kSecFormatRawKey: return "kSecFormatRawKey"; 47 case kSecFormatWrappedPKCS8: return "kSecFormatWrappedPKCS8"; 48 case kSecFormatWrappedOpenSSL: return "kSecFormatWrappedOpenSSL"; 49 case kSecFormatWrappedSSH: return "kSecFormatWrappedSSH"; 50 case kSecFormatWrappedLSH: return "kSecFormatWrappedLSH"; 51 case kSecFormatX509Cert: return "kSecFormatX509Cert"; 52 case kSecFormatPEMSequence: return "kSecFormatPEMSequence"; 53 case kSecFormatPKCS7: return "kSecFormatPKCS7"; 54 case kSecFormatPKCS12: return "kSecFormatPKCS12"; 55 case kSecFormatNetscapeCertSequence: return "kSecFormatNetscapeCertSequence"; 56 default: return "UNKNOWN FORMAT ENUM"; 57 } 58 } 59 60 const char *impExpExtItemTypeStr( 61 SecExternalItemType itemType) 62 { 63 switch(itemType) { 64 case kSecItemTypeUnknown: return "kSecItemTypeUnknown"; 65 case kSecItemTypePrivateKey: return "kSecItemTypePrivateKey"; 66 case kSecItemTypePublicKey: return "kSecItemTypePublicKey"; 67 case kSecItemTypeSessionKey: return "kSecItemTypeSessionKey"; 68 case kSecItemTypeCertificate: return "kSecItemTypeCertificate"; 69 case kSecItemTypeAggregate: return "kSecItemTypeAggregate"; 70 default: return "UNKNOWN ITEM TYPE ENUM"; 71 } 72 } 73 #endif /* NDEBUG */ 74 75 /* 76 * Parse file extension and attempt to map it to format and type. Returns true 77 * on success. 78 */ 79 bool impExpImportParseFileExten( 80 CFStringRef fstr, 81 SecExternalFormat *inputFormat, // RETURNED 82 SecExternalItemType *itemType) // RETURNED 83 { 84 if(fstr == NULL) { 85 /* nothing to work with */ 86 return false; 87 } 88 if(CFStringHasSuffix(fstr, CFSTR(".cer")) || 89 CFStringHasSuffix(fstr, CFSTR(".crt"))) { 90 *inputFormat = kSecFormatX509Cert; 91 *itemType = kSecItemTypeCertificate; 92 SecImpInferDbg("Inferring kSecFormatX509Cert from file name"); 93 return true; 94 } 95 if(CFStringHasSuffix(fstr, CFSTR(".p12")) || 96 CFStringHasSuffix(fstr, CFSTR(".pfx"))) { 97 *inputFormat = kSecFormatPKCS12; 98 *itemType = kSecItemTypeAggregate; 99 SecImpInferDbg("Inferring kSecFormatPKCS12 from file name"); 100 return true; 101 } 102 103 /* Get extension, look for key indicators as substrings */ 104 CFURLRef url = CFURLCreateWithString(NULL, fstr, NULL); 105 if(url == NULL) { 106 SecImpInferDbg("impExpImportParseFileExten: error creating URL"); 107 return false; 108 } 109 CFStringRef exten = CFURLCopyPathExtension(url); 110 CFRelease(url); 111 if(exten == NULL) { 112 /* no extension, app probably passed in only an extension */ 113 exten = fstr; 114 CFRetain(exten); 115 } 116 bool ortn = false; 117 CFRange cfr; 118 cfr = CFStringFind(exten, CFSTR("p7"), kCFCompareCaseInsensitive); 119 if(cfr.length != 0) { 120 *inputFormat = kSecFormatPKCS7; 121 *itemType = kSecItemTypeAggregate; 122 SecImpInferDbg("Inferring kSecFormatPKCS7 from file name"); 123 ortn = true; 124 } 125 if(!ortn) { 126 cfr = CFStringFind(exten, CFSTR("p8"), kCFCompareCaseInsensitive); 127 if(cfr.length != 0) { 128 *inputFormat = kSecFormatWrappedPKCS8; 129 *itemType = kSecItemTypePrivateKey; 130 SecImpInferDbg("Inferring kSecFormatPKCS8 from file name"); 131 ortn = true; 132 } 133 } 134 CFRelease(exten); 135 return ortn; 136 } 137 138 /* do a [NSString stringByDeletingPathExtension] equivalent */ 139 CFStringRef impExpImportDeleteExtension( 140 CFStringRef fileStr) 141 { 142 CFDataRef fileStrData = CFStringCreateExternalRepresentation(NULL, fileStr, 143 kCFStringEncodingUTF8, 0); 144 if(fileStrData == NULL) { 145 return NULL; 146 } 147 148 CFURLRef urlRef = CFURLCreateFromFileSystemRepresentation(NULL, 149 CFDataGetBytePtr(fileStrData), CFDataGetLength(fileStrData), false); 150 if(urlRef == NULL) { 151 CFRelease(fileStrData); 152 return NULL; 153 } 154 CFURLRef rtnUrl = CFURLCreateCopyDeletingPathExtension(NULL, urlRef); 155 CFStringRef rtnStr = NULL; 156 CFRelease(urlRef); 157 if(rtnUrl) { 158 rtnStr = CFURLGetString(rtnUrl); 159 CFRetain(rtnStr); 160 CFRelease(rtnUrl); 161 } 162 CFRelease(fileStrData); 163 return rtnStr; 164 } 165 166 #pragma mark --- mapping of external format to CDSA formats --- 167 168 /* 169 * For the record, here is the mapping of SecExternalFormat, algorithm, and key 170 * class to CSSM-style key format (CSSM_KEYBLOB_FORMAT - 171 * CSSM_KEYBLOB_RAW_FORMAT_X509, etc). The entries in the table are the 172 * last component of a CSSM_KEYBLOB_FORMAT. Format kSecFormatUnknown means 173 * "default for specified class and algorithm", which is currently the 174 * same as kSecFormatOpenSSL. 175 * 176 * algorithm/class 177 * RSA DSA DH 178 * ---------------- ---------------- ---------------- 179 * SecExternalFormat priv pub priv pub priv pub 180 * ----------------- ------- ------- ------- ------- ------- ------- 181 * kSecFormatOpenSSL PKCS1 X509 OPENSSL X509 PKCS3 X509 182 * kSecFormatBSAFE PKCS8 PKCS1 FIPS186 FIPS186 PKCS8 not supported 183 * kSecFormatUnknown PKCS1 X509 OPENSSL X509 PKCS3 X509 184 * kSecFormatSSH SSH SSH n/s n/s n/s n/s 185 * kSecFormatSSHv2 n/s SSH2 n/s SSH2 n/s n/s 186 * 187 * The only external format supported for ECDSA and ECDH keys is kSecFormatOpenSSL, 188 * which translates to OPENSSL for private keys and X509 for public keys. 189 */ 190 191 /* Arrays expressing the above table. */ 192 193 /* l.s. dimension is pub/priv for one alg */ 194 typedef struct { 195 CSSM_KEYBLOB_FORMAT priv; 196 CSSM_KEYBLOB_FORMAT pub; 197 } algForms; 198 199 /* 200 * indices into array of algForms defining all algs' formats for a given 201 * SecExternalFormat 202 */ 203 #define SIE_ALG_RSA 0 204 #define SIE_ALG_DSA 1 205 #define SIE_ALG_DH 2 206 #define SIE_ALG_ECDSA 3 207 #define SIE_ALG_LAST SIE_ALG_ECDSA 208 #define SIE_NUM_ALGS (SIE_ALG_LAST + 1) 209 210 /* kSecFormatOpenSSL */ 211 static algForms opensslAlgForms[SIE_NUM_ALGS] = 212 { 213 { CSSM_KEYBLOB_RAW_FORMAT_PKCS1, CSSM_KEYBLOB_RAW_FORMAT_X509 }, // RSA 214 { CSSM_KEYBLOB_RAW_FORMAT_OPENSSL, CSSM_KEYBLOB_RAW_FORMAT_X509 }, // DSA 215 { CSSM_KEYBLOB_RAW_FORMAT_PKCS3, CSSM_KEYBLOB_RAW_FORMAT_X509 }, // D-H 216 { CSSM_KEYBLOB_RAW_FORMAT_OPENSSL, CSSM_KEYBLOB_RAW_FORMAT_X509 }, // ECDSA 217 }; 218 219 /* kSecFormatBSAFE */ 220 static algForms bsafeAlgForms[SIE_NUM_ALGS] = 221 { 222 { CSSM_KEYBLOB_RAW_FORMAT_PKCS8, CSSM_KEYBLOB_RAW_FORMAT_PKCS1 }, // RSA 223 { CSSM_KEYBLOB_RAW_FORMAT_FIPS186, CSSM_KEYBLOB_RAW_FORMAT_FIPS186 }, // DSA 224 { CSSM_KEYBLOB_RAW_FORMAT_PKCS8, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // D-H 225 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // ECDSA not supported 226 }; 227 228 /* kSecFormatSSH (v1) */ 229 static algForms ssh1AlgForms[SIE_NUM_ALGS] = 230 { 231 { CSSM_KEYBLOB_RAW_FORMAT_OPENSSH, CSSM_KEYBLOB_RAW_FORMAT_OPENSSH }, // RSA only 232 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // DSA not supported 233 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // D-H not supported 234 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // ECDSA not supported 235 }; 236 237 /* kSecFormatSSHv2 */ 238 static algForms ssh2AlgForms[SIE_NUM_ALGS] = 239 { 240 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2 }, // RSA - public only 241 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2 }, // DSA - public only 242 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // D-H not supported 243 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // ECDSA not supported 244 }; 245 246 /* 247 * This routine performs a lookup into the above 3-dimensional array to 248 * map {algorithm, class, SecExternalFormat} to a CSSM_KEYBLOB_FORMAT. 249 * Returns errSecUnsupportedFormat in the rare appropriate case. 250 */ 251 OSStatus impExpKeyForm( 252 SecExternalFormat externForm, 253 SecExternalItemType itemType, 254 CSSM_ALGORITHMS alg, 255 CSSM_KEYBLOB_FORMAT *cssmForm, // RETURNED 256 CSSM_KEYCLASS *cssmClass) // RETRUNED 257 { 258 if(itemType == kSecItemTypeSessionKey) { 259 /* special trivial case */ 260 /* FIXME ensure caller hasn't specified bogus format */ 261 *cssmForm = CSSM_KEYBLOB_RAW_FORMAT_NONE; 262 *cssmClass = CSSM_KEYCLASS_SESSION_KEY; 263 return errSecSuccess; 264 } 265 if(externForm == kSecFormatUnknown) { 266 /* default is openssl */ 267 externForm = kSecFormatOpenSSL; 268 } 269 270 unsigned algDex; 271 switch(alg) { 272 case CSSM_ALGID_RSA: 273 algDex = SIE_ALG_RSA; 274 break; 275 case CSSM_ALGID_DSA: 276 algDex = SIE_ALG_DSA; 277 break; 278 case CSSM_ALGID_DH: 279 algDex = SIE_ALG_DH; 280 break; 281 case CSSM_ALGID_ECDSA: 282 algDex = SIE_ALG_ECDSA; 283 break; 284 default: 285 return CSSMERR_CSP_INVALID_ALGORITHM; 286 } 287 const algForms *forms; 288 switch(externForm) { 289 case kSecFormatOpenSSL: 290 forms = opensslAlgForms; 291 break; 292 case kSecFormatBSAFE: 293 forms = bsafeAlgForms; 294 break; 295 case kSecFormatSSH: 296 forms = ssh1AlgForms; 297 break; 298 case kSecFormatSSHv2: 299 forms = ssh2AlgForms; 300 break; 301 default: 302 return errSecUnsupportedFormat; 303 } 304 CSSM_KEYBLOB_FORMAT form = CSSM_KEYBLOB_RAW_FORMAT_NONE; 305 switch(itemType) { 306 case kSecItemTypePrivateKey: 307 form = forms[algDex].priv; 308 *cssmClass = CSSM_KEYCLASS_PRIVATE_KEY; 309 break; 310 case kSecItemTypePublicKey: 311 form = forms[algDex].pub; 312 *cssmClass = CSSM_KEYCLASS_PUBLIC_KEY; 313 break; 314 default: 315 return errSecUnsupportedFormat; 316 } 317 if(form == CSSM_KEYBLOB_RAW_FORMAT_NONE) { 318 /* not in the tables - abort */ 319 return errSecUnsupportedFormat; 320 } 321 else { 322 *cssmForm = form; 323 return errSecSuccess; 324 } 325 } 326 327 /* 328 * Given a raw key blob and zero to three known parameters (type, format, 329 * algorithm), figure out all parameters. Used for private and public keys. 330 */ 331 static bool impExpGuessKeyParams( 332 CFDataRef keyData, 333 SecExternalFormat *externForm, // IN/OUT 334 SecExternalItemType *itemType, // IN/OUT 335 CSSM_ALGORITHMS *keyAlg) // IN/OUT 336 { 337 /* CSSM alg list: RSA, DSA, DH, ECDSA */ 338 CSSM_ALGORITHMS minAlg = CSSM_ALGID_RSA; 339 CSSM_ALGORITHMS maxAlg = CSSM_ALGID_ECDSA; 340 SecExternalFormat minForm = kSecFormatOpenSSL; // then SSH, BSAFE, then... 341 SecExternalFormat maxForm = kSecFormatSSHv2; 342 SecExternalItemType minType = kSecItemTypePrivateKey; // just two 343 SecExternalItemType maxType = kSecItemTypePublicKey; 344 345 346 if(keyData == NULL || CFDataGetLength(keyData) == 0){ 347 MacOSError::throwMe(errSecUnsupportedKeySize); 348 } 349 350 switch(*externForm) { 351 case kSecFormatUnknown: 352 break; // run through all formats 353 case kSecFormatOpenSSL: 354 case kSecFormatSSH: 355 case kSecFormatSSHv2: 356 case kSecFormatBSAFE: 357 minForm = maxForm = *externForm; // just test this one 358 break; 359 default: 360 return false; 361 } 362 switch(*itemType) { 363 case kSecItemTypeUnknown: 364 break; 365 case kSecItemTypePrivateKey: 366 case kSecItemTypePublicKey: 367 minType = maxType = *itemType; 368 break; 369 default: 370 return false; 371 } 372 switch(*keyAlg) { 373 case CSSM_ALGID_NONE: 374 break; 375 case CSSM_ALGID_RSA: 376 case CSSM_ALGID_DSA: 377 case CSSM_ALGID_DH: 378 case CSSM_ALGID_ECDSA: 379 minAlg = maxAlg = *keyAlg; 380 break; 381 default: 382 return false; 383 } 384 385 CSSM_ALGORITHMS theAlg; 386 SecExternalFormat theForm; 387 SecExternalItemType theType; 388 CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_TRUE); 389 if(cspHand == 0) { 390 return CSSMERR_CSSM_ADDIN_LOAD_FAILED; 391 } 392 393 /* 394 * Iterate through all set of enabled {alg, type, format}. 395 * We do not assume that any of the enums are sequential hence this 396 * odd iteration algorithm.... 397 */ 398 bool ourRtn = false; 399 for(theAlg=minAlg; ; ) { 400 for(theForm=minForm; ; ) { 401 for(theType=minType; ; ) { 402 403 /* do super lightweight null unwrap to parse */ 404 OSStatus ortn = impExpImportRawKey(keyData, 405 theForm, theType, theAlg, 406 NULL, // no keychain 407 cspHand, 408 0, // no flags 409 NULL, // no key params 410 NULL, // no printName 411 NULL); // no returned items 412 if(ortn == errSecSuccess) { 413 *externForm = theForm; 414 *itemType = theType; 415 *keyAlg = theAlg; 416 ourRtn = true; 417 goto done; 418 } 419 420 /* next type or break if we're done */ 421 if(theType == maxType) { 422 break; 423 } 424 else switch(theType) { 425 case kSecItemTypePrivateKey: 426 theType = kSecItemTypePublicKey; 427 break; 428 default: 429 assert(0); 430 ourRtn = false; 431 goto done; 432 } 433 } /* for each class/type */ 434 435 /* next format or break if we're done */ 436 if(theForm == maxForm) { 437 break; 438 } 439 else switch(theForm) { 440 case kSecFormatOpenSSL: 441 theForm = kSecFormatSSH; 442 break; 443 case kSecFormatSSH: 444 theForm = kSecFormatBSAFE; 445 break; 446 case kSecFormatBSAFE: 447 theForm = kSecFormatSSHv2; 448 break; 449 default: 450 assert(0); 451 ourRtn = false; 452 goto done; 453 } 454 } /* for each format */ 455 456 /* next alg or break if we're done */ 457 if(theAlg == maxAlg) { 458 break; 459 } 460 else switch(theAlg) { 461 case CSSM_ALGID_RSA: 462 theAlg = CSSM_ALGID_DSA; 463 break; 464 case CSSM_ALGID_DSA: 465 theAlg = CSSM_ALGID_DH; 466 break; 467 case CSSM_ALGID_DH: 468 theAlg = CSSM_ALGID_ECDSA; 469 break; 470 default: 471 /* i.e. CSSM_ALGID_ECDSA, we already broke at theAlg == maxAlg */ 472 assert(0); 473 ourRtn = false; 474 goto done; 475 } 476 } /* for each alg */ 477 done: 478 cuCspDetachUnload(cspHand, CSSM_TRUE); 479 return ourRtn; 480 } 481 482 /* 483 * Guess an incoming blob's type, format and (for keys only) algorithm 484 * by examining its contents. Returns true on success, in which case 485 * *inputFormat, *itemType, and *keyAlg are all valid. Caller optionally 486 * passes in valid values any number of these as a clue. 487 */ 488 bool impExpImportGuessByExamination( 489 CFDataRef inData, 490 SecExternalFormat *inputFormat, // may be kSecFormatUnknown on entry 491 SecExternalItemType *itemType, // may be kSecItemTypeUnknown on entry 492 CSSM_ALGORITHMS *keyAlg) // CSSM_ALGID_NONE - unknown 493 { 494 if( ( (*inputFormat == kSecFormatUnknown) || 495 (*inputFormat == kSecFormatX509Cert) 496 ) && 497 ( (*itemType == kSecItemTypeUnknown) || 498 (*itemType == kSecItemTypeCertificate) ) ) { 499 /* 500 * See if it parses as a cert 501 */ 502 CSSM_CL_HANDLE clHand = cuClStartup(); 503 if(clHand == 0) { 504 return CSSMERR_CSSM_ADDIN_LOAD_FAILED; 505 } 506 CSSM_HANDLE cacheHand; 507 CSSM_RETURN crtn; 508 CSSM_DATA cdata = { int_cast<CFIndex,CSSM_SIZE>(CFDataGetLength(inData)), 509 (uint8 *)CFDataGetBytePtr(inData) }; 510 crtn = CSSM_CL_CertCache(clHand, &cdata, &cacheHand); 511 bool brtn = false; 512 if(crtn == CSSM_OK) { 513 *inputFormat = kSecFormatX509Cert; 514 *itemType = kSecItemTypeCertificate; 515 SecImpInferDbg("Inferred kSecFormatX509Cert via CL"); 516 CSSM_CL_CertAbortCache(clHand, cacheHand); 517 brtn = true; 518 } 519 cuClDetachUnload(clHand); 520 if(brtn) { 521 return true; 522 } 523 } 524 /* TBD: need way to inquire of P12 lib if this is a valid-looking PFX */ 525 526 if( ( (*inputFormat == kSecFormatUnknown) || 527 (*inputFormat == kSecFormatNetscapeCertSequence) 528 ) && 529 ( (*itemType == kSecItemTypeUnknown) || 530 (*itemType == kSecItemTypeAggregate) ) ) { 531 /* See if it's a netscape cert sequence */ 532 CSSM_RETURN crtn = impExpNetscapeCertImport(inData, 0, NULL, NULL, NULL); 533 if(crtn == CSSM_OK) { 534 *inputFormat = kSecFormatNetscapeCertSequence; 535 *itemType = kSecItemTypeAggregate; 536 SecImpInferDbg("Inferred netscape-cert-sequence by decoding"); 537 return true; 538 } 539 } 540 541 /* See if it's a key */ 542 return impExpGuessKeyParams(inData, inputFormat, itemType, keyAlg); 543 } 544 545 #pragma mark --- Key Import support --- 546 547 /* 548 * Given a context specified via a CSSM_CC_HANDLE, add a new 549 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType, 550 * AttributeLength, and an untyped pointer. 551 */ 552 CSSM_RETURN impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle, 553 uint32 AttributeType, 554 uint32 AttributeLength, 555 const void *AttributePtr) 556 { 557 CSSM_CONTEXT_ATTRIBUTE newAttr; 558 559 newAttr.AttributeType = AttributeType; 560 newAttr.AttributeLength = AttributeLength; 561 newAttr.Attribute.Data = (CSSM_DATA_PTR)AttributePtr; 562 return CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr); 563 } 564 565 /* 566 * Free memory via specified plugin's app-level allocator 567 */ 568 void impExpFreeCssmMemory( 569 CSSM_HANDLE hand, 570 void *p) 571 { 572 CSSM_API_MEMORY_FUNCS memFuncs; 573 CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs); 574 if(crtn) { 575 return; 576 } 577 memFuncs.free_func(p, memFuncs.AllocRef); 578 } 579 580 /* 581 * Calculate digest of any CSSM_KEY. Unlike older implementations 582 * of this logic, you can actually calculate the public key hash 583 * on any class of key, any format, raw CSP or CSPDL (though if 584 * you're using the CSPDL, the key has to be a reference key 585 * in that CSPDL session). 586 * 587 * Caller must free keyDigest->Data using impExpFreeCssmMemory() since 588 * this is allocated by the CSP's app-specified allocator. 589 */ 590 CSSM_RETURN impExpKeyDigest( 591 CSSM_CSP_HANDLE cspHand, 592 CSSM_KEY_PTR key, 593 CSSM_DATA_PTR keyDigest) // contents allocd and RETURNED 594 { 595 CSSM_DATA_PTR localDigest; 596 CSSM_CC_HANDLE ccHand; 597 598 CSSM_RETURN crtn = CSSM_CSP_CreatePassThroughContext(cspHand, 599 key, 600 &ccHand); 601 if(crtn) { 602 return crtn; 603 } 604 crtn = CSSM_CSP_PassThrough(ccHand, 605 CSSM_APPLECSP_KEYDIGEST, 606 NULL, 607 (void **)&localDigest); 608 if(crtn) { 609 SecImpExpDbg("CSSM_CSP_PassThrough(KEY_DIGEST) failure"); 610 } 611 else { 612 /* 613 * Give caller the Data referent and we'll free the 614 * CSSM_DATA struct itswelf. 615 */ 616 *keyDigest = *localDigest; 617 impExpFreeCssmMemory(cspHand, localDigest); 618 } 619 CSSM_DeleteContext(ccHand); 620 return crtn; 621 } 622 623 624 /* 625 * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef, 626 * return a refcounted CFStringRef suitable for use with the PKCS12 library. 627 * PKCS12 passphrases in CFData format must be UTF8 encoded. 628 */ 629 OSStatus impExpPassphraseToCFString( 630 CFTypeRef passin, 631 CFStringRef *passout) // may be the same as passin, but refcounted 632 { 633 if(CFGetTypeID(passin) == CFStringGetTypeID()) { 634 CFStringRef passInStr = (CFStringRef)passin; 635 CFRetain(passInStr); 636 *passout = passInStr; 637 return errSecSuccess; 638 } 639 else if(CFGetTypeID(passin) == CFDataGetTypeID()) { 640 CFDataRef cfData = (CFDataRef)passin; 641 CFIndex len = CFDataGetLength(cfData); 642 CFStringRef cfStr = CFStringCreateWithBytes(NULL, 643 CFDataGetBytePtr(cfData), len, kCFStringEncodingUTF8, true); 644 if(cfStr == NULL) { 645 SecImpExpDbg("Passphrase not in UTF8 format"); 646 return errSecParam; 647 } 648 *passout = cfStr; 649 return errSecSuccess; 650 } 651 else { 652 SecImpExpDbg("Passphrase not CFData or CFString"); 653 return errSecParam; 654 } 655 } 656 657 /* 658 * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef, 659 * return a refcounted CFDataRef whose bytes are suitable for use with 660 * PKCS5 (v1.5 and v2.0) key derivation. 661 */ 662 OSStatus impExpPassphraseToCFData( 663 CFTypeRef passin, 664 CFDataRef *passout) // may be the same as passin, but refcounted 665 { 666 if(CFGetTypeID(passin) == CFDataGetTypeID()) { 667 CFDataRef passInData = (CFDataRef)passin; 668 CFRetain(passInData); 669 *passout = passInData; 670 return errSecSuccess; 671 } 672 else if(CFGetTypeID(passin) == CFStringGetTypeID()) { 673 CFStringRef passInStr = (CFStringRef)passin; 674 CFDataRef outData; 675 outData = CFStringCreateExternalRepresentation(NULL, 676 passInStr, 677 kCFStringEncodingUTF8, 678 0); // lossByte 0 ==> no loss allowed 679 if(outData == NULL) { 680 /* Well try with lossy conversion */ 681 SecImpExpDbg("Trying lossy conversion of CFString passphrase to UTF8"); 682 outData = CFStringCreateExternalRepresentation(NULL, 683 passInStr, 684 kCFStringEncodingUTF8, 685 1); 686 if(outData == NULL) { 687 SecImpExpDbg("Failure on conversion of CFString passphrase to UTF8"); 688 /* what do we do now, Batman? */ 689 return errSecParam; 690 } 691 } 692 *passout = outData; 693 return errSecSuccess; 694 } 695 else { 696 SecImpExpDbg("Passphrase not CFData or CFString"); 697 return errSecParam; 698 } 699 } 700 701 /* 702 * Add a CFString to a crypto context handle. 703 */ 704 static CSSM_RETURN impExpAddStringAttr( 705 CSSM_CC_HANDLE ccHand, 706 CFStringRef str, 707 CSSM_ATTRIBUTE_TYPE attrType) 708 { 709 /* CFStrings are passed as external rep in UTF8 encoding by convention */ 710 CFDataRef outData; 711 outData = CFStringCreateExternalRepresentation(NULL, 712 str, kCFStringEncodingUTF8, 0); // lossByte 0 ==> no loss allowed 713 if(outData == NULL) { 714 SecImpExpDbg("impExpAddStringAttr: bad string format"); 715 return errSecParam; 716 } 717 718 CSSM_DATA attrData; 719 attrData.Data = (uint8 *)CFDataGetBytePtr(outData); 720 attrData.Length = CFDataGetLength(outData); 721 CSSM_RETURN crtn = impExpAddContextAttribute(ccHand, attrType, sizeof(CSSM_DATA), 722 &attrData); 723 CFRelease(outData); 724 if(crtn) { 725 SecImpExpDbg("impExpAddStringAttr: CSSM_UpdateContextAttributes error"); 726 } 727 return crtn; 728 } 729 730 /* 731 * Generate a secure passphrase key. Caller must eventually CSSM_FreeKey the result. 732 */ 733 static CSSM_RETURN impExpCreatePassKey( 734 const SecKeyImportExportParameters *keyParams, // required 735 CSSM_CSP_HANDLE cspHand, // MUST be CSPDL 736 impExpVerifyPhrase verifyPhrase, // for secure passphrase 737 CSSM_KEY_PTR *passKey) // mallocd and RETURNED 738 { 739 CSSM_RETURN crtn; 740 CSSM_CC_HANDLE ccHand; 741 uint32 verifyAttr; 742 CSSM_DATA dummyLabel; 743 CSSM_KEY_PTR ourKey = NULL; 744 745 SecImpExpDbg("Generating secure passphrase key"); 746 ourKey = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY)); 747 if(ourKey == NULL) { 748 return errSecAllocate; 749 } 750 memset(ourKey, 0, sizeof(CSSM_KEY)); 751 752 crtn = CSSM_CSP_CreateKeyGenContext(cspHand, 753 CSSM_ALGID_SECURE_PASSPHRASE, 754 4, // keySizeInBits must be non zero 755 NULL, // Seed 756 NULL, // Salt 757 NULL, // StartDate 758 NULL, // EndDate 759 NULL, // Params 760 &ccHand); 761 if(crtn) { 762 SecImpExpDbg("impExpCreatePassKey: CSSM_CSP_CreateKeyGenContext error"); 763 free(ourKey); 764 return crtn; 765 } 766 /* subsequent errors to errOut: */ 767 768 /* additional context attributes specific to this type of key gen */ 769 assert(keyParams != NULL); // or we wouldn't be here 770 if(keyParams->alertTitle != NULL) { 771 crtn = impExpAddStringAttr(ccHand, keyParams->alertTitle, 772 CSSM_ATTRIBUTE_ALERT_TITLE); 773 if(crtn) { 774 goto errOut; 775 } 776 } 777 if(keyParams->alertPrompt != NULL) { 778 crtn = impExpAddStringAttr(ccHand, keyParams->alertPrompt, 779 CSSM_ATTRIBUTE_PROMPT); 780 if(crtn) { 781 goto errOut; 782 } 783 } 784 verifyAttr = (verifyPhrase == VP_Export) ? 1 : 0; 785 crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_VERIFY_PASSPHRASE, 786 sizeof(uint32), (const void *)((size_t) verifyAttr)); 787 if(crtn) { 788 SecImpExpDbg("impExpCreatePassKey: CSSM_UpdateContextAttributes error"); 789 goto errOut; 790 } 791 792 dummyLabel.Data = (uint8 *)"Secure Passphrase"; 793 dummyLabel.Length = strlen((char *)dummyLabel.Data); 794 795 crtn = CSSM_GenerateKey(ccHand, 796 CSSM_KEYUSE_ANY, 797 CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE, 798 &dummyLabel, 799 NULL, // ACL 800 ourKey); 801 if(crtn) { 802 SecImpExpDbg("impExpCreatePassKey: CSSM_GenerateKey error"); 803 } 804 errOut: 805 CSSM_DeleteContext(ccHand); 806 if(crtn == CSSM_OK) { 807 *passKey = ourKey; 808 } 809 else if(ourKey != NULL) { 810 free(ourKey); 811 } 812 return crtn; 813 } 814 815 /* 816 * Obtain passphrase, given a SecKeyImportExportParameters. 817 * 818 * Passphrase comes from one of two places: app-specified, in 819 * SecKeyImportExportParameters.passphrase (either as CFStringRef 820 * or CFDataRef); or via the secure passphrase mechanism. 821 * 822 * Passphrase is returned in AT MOST one of two forms: 823 * 824 * -- Secure passphrase is returned as a CSSM_KEY_PTR, which the 825 * caller must CSSM_FreeKey later as well as free()ing the actual 826 * CSSM_KEY_PTR. 827 * -- CFTypeRef for app-supplied passphrases. This can be one of 828 * two types: 829 * 830 * -- CFStringRef, for use with P12 831 * -- CFDataRef, for more general use (e.g. for PKCS5). 832 * 833 * In either case the caller must CFRelease the result. 834 */ 835 OSStatus impExpPassphraseCommon( 836 const SecKeyImportExportParameters *keyParams, 837 CSSM_CSP_HANDLE cspHand, // MUST be CSPDL, for passKey generation 838 impExpPassphraseForm phraseForm, 839 impExpVerifyPhrase verifyPhrase, // for secure passphrase 840 CFTypeRef *phrase, // RETURNED, or 841 CSSM_KEY_PTR *passKey) // mallocd and RETURNED 842 { 843 assert(keyParams != NULL); 844 845 /* Give precedence to secure passphrase */ 846 if(keyParams->flags & kSecKeySecurePassphrase) { 847 assert(passKey != NULL); 848 return impExpCreatePassKey(keyParams, cspHand, verifyPhrase, passKey); 849 } 850 else if(keyParams->passphrase != NULL) { 851 CFTypeRef phraseOut; 852 OSStatus ortn; 853 assert(phrase != NULL); 854 switch(phraseForm) { 855 case SPF_String: 856 ortn = impExpPassphraseToCFString(keyParams->passphrase, 857 (CFStringRef *)&phraseOut); 858 break; 859 case SPF_Data: 860 ortn = impExpPassphraseToCFData(keyParams->passphrase, 861 (CFDataRef *)&phraseOut); 862 break; 863 default: 864 /* only called internally */ 865 assert(0); 866 ortn = errSecParam; 867 } 868 if(ortn == errSecSuccess) { 869 *phrase = phraseOut; 870 } 871 return ortn; 872 } 873 else { 874 return errSecPassphraseRequired; 875 } 876 } 877 878 static void ToggleKeyAttribute( 879 CFArrayRef keyAttrs, 880 CFTypeRef attr, 881 CSSM_KEYATTR_FLAGS mask, 882 CSSM_KEYATTR_FLAGS &result) 883 { 884 // If the keyAttrs array contains the given attribute, 885 // set the corresponding keyattr flags, otherwise clear them. 886 // (Note: caller verifies that keyAttrs is not NULL.) 887 CFIndex numItems = CFArrayGetCount(keyAttrs); 888 result &= ~(mask); 889 if (numItems > 0) { 890 CFRange range = CFRangeMake(0, numItems); 891 if (CFArrayContainsValue(keyAttrs, range, attr)) 892 result |= mask; 893 } 894 } 895 896 CSSM_KEYATTR_FLAGS ConvertArrayToKeyAttributes(SecKeyRef aKey, CFArrayRef keyAttrs) 897 { 898 CSSM_KEYATTR_FLAGS result = CSSM_KEYATTR_RETURN_DEFAULT; 899 900 if (aKey) { 901 const CSSM_KEY* cssmKey = NULL; 902 if (errSecSuccess == SecKeyGetCSSMKey(aKey, &cssmKey)) 903 result = cssmKey->KeyHeader.KeyAttr; 904 } 905 906 if (!keyAttrs) 907 return result; 908 909 CFMutableArrayRef attrs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 910 CFIndex idx, count = CFArrayGetCount(keyAttrs); 911 for (idx=0; idx<count; idx++) { 912 CFTypeRef attr = (CFTypeRef) CFArrayGetValueAtIndex(keyAttrs, idx); 913 if (attr && (CFNumberGetTypeID() == CFGetTypeID(attr))) { 914 // Convert numeric CSSM keyattr values to equivalent attribute constants 915 uint32 value; 916 if (CFNumberGetValue((CFNumberRef)attr, kCFNumberSInt32Type, &value)) { 917 switch (value) { 918 case CSSM_KEYATTR_SENSITIVE: 919 attr = kSecAttrIsSensitive; 920 break; 921 case CSSM_KEYATTR_EXTRACTABLE: 922 attr = kSecAttrIsExtractable; 923 break; 924 case CSSM_KEYATTR_PERMANENT: 925 attr = kSecAttrIsPermanent; 926 break; 927 default: 928 attr = NULL; 929 break; 930 } 931 } 932 } 933 if (attr) 934 CFArrayAppendValue(attrs, attr); 935 } 936 937 // Set key attribute flag in result if present in the array, otherwise clear 938 ToggleKeyAttribute(attrs, kSecAttrIsSensitive, CSSM_KEYATTR_SENSITIVE, result); 939 ToggleKeyAttribute(attrs, kSecAttrIsExtractable, CSSM_KEYATTR_EXTRACTABLE, result); 940 ToggleKeyAttribute(attrs, kSecAttrIsPermanent, CSSM_KEYATTR_PERMANENT, result); 941 942 // if caller specified an attributes array which omitted kSecAttrIsExtractable, 943 // this implies the sensitive attribute; force it on so that at least one bit 944 // is set. If our result is 0, this is indistinguishable from the case where 945 // no key attributes were specified, causing a default bitmask to be used 946 // in subsequent import code. 947 948 if (0==(result & CSSM_KEYATTR_EXTRACTABLE)) 949 result |= CSSM_KEYATTR_SENSITIVE; 950 951 CFRelease(attrs); 952 return result; 953 } 954 955 Boolean ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(SecKeyRef aKey, 956 const SecItemImportExportKeyParameters* newPtr, SecKeyImportExportParameters* oldPtr) 957 { 958 Boolean result = false; 959 960 if (NULL != oldPtr && NULL != newPtr) 961 { 962 oldPtr->version = newPtr->version; 963 oldPtr->flags = newPtr->flags; 964 oldPtr->passphrase = newPtr->passphrase; 965 oldPtr->alertTitle = newPtr->alertTitle; 966 oldPtr->alertPrompt = newPtr->alertPrompt; 967 oldPtr->accessRef = newPtr->accessRef; 968 oldPtr->keyUsage = ConvertArrayToKeyUsage(newPtr->keyUsage); 969 oldPtr->keyAttributes = ConvertArrayToKeyAttributes(aKey, newPtr->keyAttributes); 970 result = true; 971 } 972 return result; 973 } 974