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 "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 345 if(keyData == NULL || CFDataGetLength(keyData) == 0){ 346 MacOSError::throwMe(errSecUnsupportedKeySize); 347 } 348 349 switch(*externForm) { 350 case kSecFormatUnknown: 351 break; // run through all formats 352 case kSecFormatOpenSSL: 353 case kSecFormatSSH: 354 case kSecFormatSSHv2: 355 case kSecFormatBSAFE: 356 minForm = maxForm = *externForm; // just test this one 357 break; 358 default: 359 return false; 360 } 361 switch(*itemType) { 362 case kSecItemTypeUnknown: 363 break; 364 case kSecItemTypePrivateKey: 365 case kSecItemTypePublicKey: 366 minType = maxType = *itemType; 367 break; 368 default: 369 return false; 370 } 371 switch(*keyAlg) { 372 case CSSM_ALGID_NONE: 373 break; 374 case CSSM_ALGID_RSA: 375 case CSSM_ALGID_DSA: 376 case CSSM_ALGID_DH: 377 case CSSM_ALGID_ECDSA: 378 minAlg = maxAlg = *keyAlg; 379 break; 380 default: 381 return false; 382 } 383 384 CSSM_ALGORITHMS theAlg; 385 SecExternalFormat theForm; 386 SecExternalItemType theType; 387 CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_TRUE); 388 if(cspHand == 0) { 389 return CSSMERR_CSSM_ADDIN_LOAD_FAILED; 390 } 391 392 /* 393 * Iterate through all set of enabled {alg, type, format}. 394 * We do not assume that any of the enums are sequential hence this 395 * odd iteration algorithm.... 396 */ 397 bool ourRtn = false; 398 for(theAlg=minAlg; ; ) { 399 for(theForm=minForm; ; ) { 400 for(theType=minType; ; ) { 401 402 /* do super lightweight null unwrap to parse */ 403 OSStatus ortn = impExpImportRawKey(keyData, 404 theForm, theType, theAlg, 405 NULL, // no keychain 406 cspHand, 407 0, // no flags 408 NULL, // no key params 409 NULL, // no printName 410 NULL); // no returned items 411 if(ortn == errSecSuccess) { 412 *externForm = theForm; 413 *itemType = theType; 414 *keyAlg = theAlg; 415 ourRtn = true; 416 goto done; 417 } 418 419 /* next type or break if we're done */ 420 if(theType == maxType) { 421 break; 422 } 423 else switch(theType) { 424 case kSecItemTypePrivateKey: 425 theType = kSecItemTypePublicKey; 426 break; 427 default: 428 assert(0); 429 ourRtn = false; 430 goto done; 431 } 432 } /* for each class/type */ 433 434 /* next format or break if we're done */ 435 if(theForm == maxForm) { 436 break; 437 } 438 else switch(theForm) { 439 case kSecFormatOpenSSL: 440 theForm = kSecFormatSSH; 441 break; 442 case kSecFormatSSH: 443 theForm = kSecFormatBSAFE; 444 break; 445 case kSecFormatBSAFE: 446 theForm = kSecFormatSSHv2; 447 break; 448 default: 449 assert(0); 450 ourRtn = false; 451 goto done; 452 } 453 } /* for each format */ 454 455 /* next alg or break if we're done */ 456 if(theAlg == maxAlg) { 457 break; 458 } 459 else switch(theAlg) { 460 case CSSM_ALGID_RSA: 461 theAlg = CSSM_ALGID_DSA; 462 break; 463 case CSSM_ALGID_DSA: 464 theAlg = CSSM_ALGID_DH; 465 break; 466 case CSSM_ALGID_DH: 467 theAlg = CSSM_ALGID_ECDSA; 468 break; 469 default: 470 /* i.e. CSSM_ALGID_ECDSA, we already broke at theAlg == maxAlg */ 471 assert(0); 472 ourRtn = false; 473 goto done; 474 } 475 } /* for each alg */ 476done: 477 cuCspDetachUnload(cspHand, CSSM_TRUE); 478 return ourRtn; 479} 480 481/* 482 * Guess an incoming blob's type, format and (for keys only) algorithm 483 * by examining its contents. Returns true on success, in which case 484 * *inputFormat, *itemType, and *keyAlg are all valid. Caller optionally 485 * passes in valid values any number of these as a clue. 486 */ 487bool impExpImportGuessByExamination( 488 CFDataRef inData, 489 SecExternalFormat *inputFormat, // may be kSecFormatUnknown on entry 490 SecExternalItemType *itemType, // may be kSecItemTypeUnknown on entry 491 CSSM_ALGORITHMS *keyAlg) // CSSM_ALGID_NONE - unknown 492{ 493 if( ( (*inputFormat == kSecFormatUnknown) || 494 (*inputFormat == kSecFormatX509Cert) 495 ) && 496 ( (*itemType == kSecItemTypeUnknown) || 497 (*itemType == kSecItemTypeCertificate) ) ) { 498 /* 499 * See if it parses as a cert 500 */ 501 CSSM_CL_HANDLE clHand = cuClStartup(); 502 if(clHand == 0) { 503 return CSSMERR_CSSM_ADDIN_LOAD_FAILED; 504 } 505 CSSM_HANDLE cacheHand; 506 CSSM_RETURN crtn; 507 CSSM_DATA cdata = { CFDataGetLength(inData), 508 (uint8 *)CFDataGetBytePtr(inData) }; 509 crtn = CSSM_CL_CertCache(clHand, &cdata, &cacheHand); 510 bool brtn = false; 511 if(crtn == CSSM_OK) { 512 *inputFormat = kSecFormatX509Cert; 513 *itemType = kSecItemTypeCertificate; 514 SecImpInferDbg("Inferred kSecFormatX509Cert via CL"); 515 CSSM_CL_CertAbortCache(clHand, cacheHand); 516 brtn = true; 517 } 518 cuClDetachUnload(clHand); 519 if(brtn) { 520 return true; 521 } 522 } 523 /* TBD: need way to inquire of P12 lib if this is a valid-looking PFX */ 524 525 if( ( (*inputFormat == kSecFormatUnknown) || 526 (*inputFormat == kSecFormatNetscapeCertSequence) 527 ) && 528 ( (*itemType == kSecItemTypeUnknown) || 529 (*itemType == kSecItemTypeAggregate) ) ) { 530 /* See if it's a netscape cert sequence */ 531 CSSM_RETURN crtn = impExpNetscapeCertImport(inData, 0, NULL, NULL, NULL); 532 if(crtn == CSSM_OK) { 533 *inputFormat = kSecFormatNetscapeCertSequence; 534 *itemType = kSecItemTypeAggregate; 535 SecImpInferDbg("Inferred netscape-cert-sequence by decoding"); 536 return true; 537 } 538 } 539 540 /* See if it's a key */ 541 return impExpGuessKeyParams(inData, inputFormat, itemType, keyAlg); 542} 543 544#pragma mark --- Key Import support --- 545 546/* 547 * Given a context specified via a CSSM_CC_HANDLE, add a new 548 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType, 549 * AttributeLength, and an untyped pointer. 550 */ 551CSSM_RETURN impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle, 552 uint32 AttributeType, 553 uint32 AttributeLength, 554 const void *AttributePtr) 555{ 556 CSSM_CONTEXT_ATTRIBUTE newAttr; 557 558 newAttr.AttributeType = AttributeType; 559 newAttr.AttributeLength = AttributeLength; 560 newAttr.Attribute.Data = (CSSM_DATA_PTR)AttributePtr; 561 return CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr); 562} 563 564/* 565 * Free memory via specified plugin's app-level allocator 566 */ 567void impExpFreeCssmMemory( 568 CSSM_HANDLE hand, 569 void *p) 570{ 571 CSSM_API_MEMORY_FUNCS memFuncs; 572 CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs); 573 if(crtn) { 574 return; 575 } 576 memFuncs.free_func(p, memFuncs.AllocRef); 577} 578 579/* 580 * Calculate digest of any CSSM_KEY. Unlike older implementations 581 * of this logic, you can actually calculate the public key hash 582 * on any class of key, any format, raw CSP or CSPDL (though if 583 * you're using the CSPDL, the key has to be a reference key 584 * in that CSPDL session). 585 * 586 * Caller must free keyDigest->Data using impExpFreeCssmMemory() since 587 * this is allocated by the CSP's app-specified allocator. 588 */ 589CSSM_RETURN impExpKeyDigest( 590 CSSM_CSP_HANDLE cspHand, 591 CSSM_KEY_PTR key, 592 CSSM_DATA_PTR keyDigest) // contents allocd and RETURNED 593{ 594 CSSM_DATA_PTR localDigest; 595 CSSM_CC_HANDLE ccHand; 596 597 CSSM_RETURN crtn = CSSM_CSP_CreatePassThroughContext(cspHand, 598 key, 599 &ccHand); 600 if(crtn) { 601 return crtn; 602 } 603 crtn = CSSM_CSP_PassThrough(ccHand, 604 CSSM_APPLECSP_KEYDIGEST, 605 NULL, 606 (void **)&localDigest); 607 if(crtn) { 608 SecImpExpDbg("CSSM_CSP_PassThrough(KEY_DIGEST) failure"); 609 } 610 else { 611 /* 612 * Give caller the Data referent and we'll free the 613 * CSSM_DATA struct itswelf. 614 */ 615 *keyDigest = *localDigest; 616 impExpFreeCssmMemory(cspHand, localDigest); 617 } 618 CSSM_DeleteContext(ccHand); 619 return crtn; 620} 621 622 623/* 624 * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef, 625 * return a refcounted CFStringRef suitable for use with the PKCS12 library. 626 * PKCS12 passphrases in CFData format must be UTF8 encoded. 627 */ 628OSStatus impExpPassphraseToCFString( 629 CFTypeRef passin, 630 CFStringRef *passout) // may be the same as passin, but refcounted 631{ 632 if(CFGetTypeID(passin) == CFStringGetTypeID()) { 633 CFStringRef passInStr = (CFStringRef)passin; 634 CFRetain(passInStr); 635 *passout = passInStr; 636 return errSecSuccess; 637 } 638 else if(CFGetTypeID(passin) == CFDataGetTypeID()) { 639 CFDataRef cfData = (CFDataRef)passin; 640 CFIndex len = CFDataGetLength(cfData); 641 CFStringRef cfStr = CFStringCreateWithBytes(NULL, 642 CFDataGetBytePtr(cfData), len, kCFStringEncodingUTF8, true); 643 if(cfStr == NULL) { 644 SecImpExpDbg("Passphrase not in UTF8 format"); 645 return errSecParam; 646 } 647 *passout = cfStr; 648 return errSecSuccess; 649 } 650 else { 651 SecImpExpDbg("Passphrase not CFData or CFString"); 652 return errSecParam; 653 } 654} 655 656/* 657 * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef, 658 * return a refcounted CFDataRef whose bytes are suitable for use with 659 * PKCS5 (v1.5 and v2.0) key derivation. 660 */ 661OSStatus impExpPassphraseToCFData( 662 CFTypeRef passin, 663 CFDataRef *passout) // may be the same as passin, but refcounted 664{ 665 if(CFGetTypeID(passin) == CFDataGetTypeID()) { 666 CFDataRef passInData = (CFDataRef)passin; 667 CFRetain(passInData); 668 *passout = passInData; 669 return errSecSuccess; 670 } 671 else if(CFGetTypeID(passin) == CFStringGetTypeID()) { 672 CFStringRef passInStr = (CFStringRef)passin; 673 CFDataRef outData; 674 outData = CFStringCreateExternalRepresentation(NULL, 675 passInStr, 676 kCFStringEncodingUTF8, 677 0); // lossByte 0 ==> no loss allowed 678 if(outData == NULL) { 679 /* Well try with lossy conversion */ 680 SecImpExpDbg("Trying lossy conversion of CFString passphrase to UTF8"); 681 outData = CFStringCreateExternalRepresentation(NULL, 682 passInStr, 683 kCFStringEncodingUTF8, 684 1); 685 if(outData == NULL) { 686 SecImpExpDbg("Failure on conversion of CFString passphrase to UTF8"); 687 /* what do we do now, Batman? */ 688 return errSecParam; 689 } 690 } 691 *passout = outData; 692 return errSecSuccess; 693 } 694 else { 695 SecImpExpDbg("Passphrase not CFData or CFString"); 696 return errSecParam; 697 } 698} 699 700/* 701* Add a CFString to a crypto context handle. 702*/ 703static CSSM_RETURN impExpAddStringAttr( 704 CSSM_CC_HANDLE ccHand, 705 CFStringRef str, 706 CSSM_ATTRIBUTE_TYPE attrType) 707{ 708 /* CFStrings are passed as external rep in UTF8 encoding by convention */ 709 CFDataRef outData; 710 outData = CFStringCreateExternalRepresentation(NULL, 711 str, kCFStringEncodingUTF8, 0); // lossByte 0 ==> no loss allowed 712 if(outData == NULL) { 713 SecImpExpDbg("impExpAddStringAttr: bad string format"); 714 return errSecParam; 715 } 716 717 CSSM_DATA attrData; 718 attrData.Data = (uint8 *)CFDataGetBytePtr(outData); 719 attrData.Length = CFDataGetLength(outData); 720 CSSM_RETURN crtn = impExpAddContextAttribute(ccHand, attrType, sizeof(CSSM_DATA), 721 &attrData); 722 CFRelease(outData); 723 if(crtn) { 724 SecImpExpDbg("impExpAddStringAttr: CSSM_UpdateContextAttributes error"); 725 } 726 return crtn; 727} 728 729/* 730 * Generate a secure passphrase key. Caller must eventually CSSM_FreeKey the result. 731 */ 732static CSSM_RETURN impExpCreatePassKey( 733 const SecKeyImportExportParameters *keyParams, // required 734 CSSM_CSP_HANDLE cspHand, // MUST be CSPDL 735 impExpVerifyPhrase verifyPhrase, // for secure passphrase 736 CSSM_KEY_PTR *passKey) // mallocd and RETURNED 737{ 738 CSSM_RETURN crtn; 739 CSSM_CC_HANDLE ccHand; 740 uint32 verifyAttr; 741 CSSM_DATA dummyLabel; 742 CSSM_KEY_PTR ourKey = NULL; 743 744 SecImpExpDbg("Generating secure passphrase key"); 745 ourKey = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY)); 746 if(ourKey == NULL) { 747 return errSecAllocate; 748 } 749 memset(ourKey, 0, sizeof(CSSM_KEY)); 750 751 crtn = CSSM_CSP_CreateKeyGenContext(cspHand, 752 CSSM_ALGID_SECURE_PASSPHRASE, 753 4, // keySizeInBits must be non zero 754 NULL, // Seed 755 NULL, // Salt 756 NULL, // StartDate 757 NULL, // EndDate 758 NULL, // Params 759 &ccHand); 760 if(crtn) { 761 SecImpExpDbg("impExpCreatePassKey: CSSM_CSP_CreateKeyGenContext error"); 762 return crtn; 763 } 764 /* subsequent errors to errOut: */ 765 766 /* additional context attributes specific to this type of key gen */ 767 assert(keyParams != NULL); // or we wouldn't be here 768 if(keyParams->alertTitle != NULL) { 769 crtn = impExpAddStringAttr(ccHand, keyParams->alertTitle, 770 CSSM_ATTRIBUTE_ALERT_TITLE); 771 if(crtn) { 772 goto errOut; 773 } 774 } 775 if(keyParams->alertPrompt != NULL) { 776 crtn = impExpAddStringAttr(ccHand, keyParams->alertPrompt, 777 CSSM_ATTRIBUTE_PROMPT); 778 if(crtn) { 779 goto errOut; 780 } 781 } 782 verifyAttr = (verifyPhrase == VP_Export) ? 1 : 0; 783 crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_VERIFY_PASSPHRASE, 784 sizeof(uint32), (const void *)((size_t) verifyAttr)); 785 if(crtn) { 786 SecImpExpDbg("impExpCreatePassKey: CSSM_UpdateContextAttributes error"); 787 goto errOut; 788 } 789 790 dummyLabel.Data = (uint8 *)"Secure Passphrase"; 791 dummyLabel.Length = strlen((char *)dummyLabel.Data); 792 793 crtn = CSSM_GenerateKey(ccHand, 794 CSSM_KEYUSE_ANY, 795 CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE, 796 &dummyLabel, 797 NULL, // ACL 798 ourKey); 799 if(crtn) { 800 SecImpExpDbg("impExpCreatePassKey: CSSM_GenerateKey error"); 801 } 802errOut: 803 CSSM_DeleteContext(ccHand); 804 if(crtn == CSSM_OK) { 805 *passKey = ourKey; 806 } 807 else if(ourKey != NULL) { 808 free(ourKey); 809 } 810 return crtn; 811} 812 813/* 814 * Obtain passphrase, given a SecKeyImportExportParameters. 815 * 816 * Passphrase comes from one of two places: app-specified, in 817 * SecKeyImportExportParameters.passphrase (either as CFStringRef 818 * or CFDataRef); or via the secure passphrase mechanism. 819 * 820 * Passphrase is returned in AT MOST one of two forms: 821 * 822 * -- Secure passphrase is returned as a CSSM_KEY_PTR, which the 823 * caller must CSSM_FreeKey later as well as free()ing the actual 824 * CSSM_KEY_PTR. 825 * -- CFTypeRef for app-supplied passphrases. This can be one of 826 * two types: 827 * 828 * -- CFStringRef, for use with P12 829 * -- CFDataRef, for more general use (e.g. for PKCS5). 830 * 831 * In either case the caller must CFRelease the result. 832 */ 833OSStatus impExpPassphraseCommon( 834 const SecKeyImportExportParameters *keyParams, 835 CSSM_CSP_HANDLE cspHand, // MUST be CSPDL, for passKey generation 836 impExpPassphraseForm phraseForm, 837 impExpVerifyPhrase verifyPhrase, // for secure passphrase 838 CFTypeRef *phrase, // RETURNED, or 839 CSSM_KEY_PTR *passKey) // mallocd and RETURNED 840{ 841 assert(keyParams != NULL); 842 843 /* Give precedence to secure passphrase */ 844 if(keyParams->flags & kSecKeySecurePassphrase) { 845 assert(passKey != NULL); 846 return impExpCreatePassKey(keyParams, cspHand, verifyPhrase, passKey); 847 } 848 else if(keyParams->passphrase != NULL) { 849 CFTypeRef phraseOut; 850 OSStatus ortn; 851 assert(phrase != NULL); 852 switch(phraseForm) { 853 case SPF_String: 854 ortn = impExpPassphraseToCFString(keyParams->passphrase, 855 (CFStringRef *)&phraseOut); 856 break; 857 case SPF_Data: 858 ortn = impExpPassphraseToCFData(keyParams->passphrase, 859 (CFDataRef *)&phraseOut); 860 break; 861 default: 862 /* only called internally */ 863 assert(0); 864 ortn = errSecParam; 865 } 866 if(ortn == errSecSuccess) { 867 *phrase = phraseOut; 868 } 869 return ortn; 870 } 871 else { 872 return errSecPassphraseRequired; 873 } 874} 875 876static void ToggleKeyAttribute( 877 CFArrayRef keyAttrs, 878 CFTypeRef attr, 879 CSSM_KEYATTR_FLAGS mask, 880 CSSM_KEYATTR_FLAGS &result) 881{ 882 // If the keyAttrs array contains the given attribute, 883 // set the corresponding keyattr flags, otherwise clear them. 884 // (Note: caller verifies that keyAttrs is not NULL.) 885 CFIndex numItems = CFArrayGetCount(keyAttrs); 886 result &= ~(mask); 887 if (numItems > 0) { 888 CFRange range = CFRangeMake(0, numItems); 889 if (CFArrayContainsValue(keyAttrs, range, attr)) 890 result |= mask; 891 } 892} 893 894CSSM_KEYATTR_FLAGS ConvertArrayToKeyAttributes(SecKeyRef aKey, CFArrayRef keyAttrs) 895{ 896 CSSM_KEYATTR_FLAGS result = CSSM_KEYATTR_RETURN_DEFAULT; 897 898 if (aKey) { 899 const CSSM_KEY* cssmKey = NULL; 900 if (errSecSuccess == SecKeyGetCSSMKey(aKey, &cssmKey)) 901 result = cssmKey->KeyHeader.KeyAttr; 902 } 903 904 if (!keyAttrs) 905 return result; 906 907 CFMutableArrayRef attrs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 908 CFIndex idx, count = CFArrayGetCount(keyAttrs); 909 for (idx=0; idx<count; idx++) { 910 CFTypeRef attr = (CFTypeRef) CFArrayGetValueAtIndex(keyAttrs, idx); 911 if (attr && (CFNumberGetTypeID() == CFGetTypeID(attr))) { 912 // Convert numeric CSSM keyattr values to equivalent attribute constants 913 uint32 value; 914 if (CFNumberGetValue((CFNumberRef)attr, kCFNumberSInt32Type, &value)) { 915 switch (value) { 916 case CSSM_KEYATTR_SENSITIVE: 917 attr = kSecAttrIsSensitive; 918 break; 919 case CSSM_KEYATTR_EXTRACTABLE: 920 attr = kSecAttrIsExtractable; 921 break; 922 case CSSM_KEYATTR_PERMANENT: 923 attr = kSecAttrIsPermanent; 924 break; 925 default: 926 attr = NULL; 927 break; 928 } 929 } 930 } 931 if (attr) 932 CFArrayAppendValue(attrs, attr); 933 } 934 935 // Set key attribute flag in result if present in the array, otherwise clear 936 ToggleKeyAttribute(attrs, kSecAttrIsSensitive, CSSM_KEYATTR_SENSITIVE, result); 937 ToggleKeyAttribute(attrs, kSecAttrIsExtractable, CSSM_KEYATTR_EXTRACTABLE, result); 938 ToggleKeyAttribute(attrs, kSecAttrIsPermanent, CSSM_KEYATTR_PERMANENT, result); 939 940 // if caller specified an attributes array which omitted kSecAttrIsExtractable, 941 // this implies the sensitive attribute; force it on so that at least one bit 942 // is set. If our result is 0, this is indistinguishable from the case where 943 // no key attributes were specified, causing a default bitmask to be used 944 // in subsequent import code. 945 946 if (0==(result & CSSM_KEYATTR_EXTRACTABLE)) 947 result |= CSSM_KEYATTR_SENSITIVE; 948 949 CFRelease(attrs); 950 return result; 951} 952 953Boolean ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(SecKeyRef aKey, 954 const SecItemImportExportKeyParameters* newPtr, SecKeyImportExportParameters* oldPtr) 955{ 956 Boolean result = false; 957 958 if (NULL != oldPtr && NULL != newPtr) 959 { 960 oldPtr->version = newPtr->version; 961 oldPtr->flags = newPtr->flags; 962 oldPtr->passphrase = newPtr->passphrase; 963 oldPtr->alertTitle = newPtr->alertTitle; 964 oldPtr->alertPrompt = newPtr->alertPrompt; 965 oldPtr->accessRef = newPtr->accessRef; 966 oldPtr->keyUsage = ConvertArrayToKeyUsage(newPtr->keyUsage); 967 oldPtr->keyAttributes = ConvertArrayToKeyAttributes(aKey, newPtr->keyAttributes); 968 result = true; 969 } 970 return result; 971} 972 973