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