1/* 2 * Copyright (c) 2003-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/* 25 * pkcs12Utils.cpp 26 */ 27 28#include "pkcs12Utils.h" 29#include <string.h> 30#include "pkcs7Templates.h" 31#include "pkcs12Templates.h" 32#include "pkcs12Crypto.h" 33#include "pkcs12Debug.h" 34#include <security_asn1/nssUtils.h> 35#include <Security/secasn1t.h> 36#include <security_utilities/devrandom.h> 37#include <security_utilities/errors.h> 38#include <security_cdsa_utils/cuCdsaUtils.h> 39#include <Security/oidsattr.h> 40#include <Security/oidsalg.h> 41#include <Security/cssmapple.h> 42 43/* malloc a NULL-ed array of pointers of size num+1 */ 44void **p12NssNullArray( 45 uint32 num, 46 SecNssCoder &coder) 47{ 48 unsigned len = (num + 1) * sizeof(void *); 49 void **p = (void **)coder.malloc(len); 50 memset(p, 0, len); 51 return p; 52} 53 54/* CSSM_DATA --> uint32. Returns true if OK. */ 55bool p12DataToInt( 56 const CSSM_DATA &cdata, 57 uint32 &u) 58{ 59 if((cdata.Length == 0) || (cdata.Data == NULL)) { 60 /* default/not present */ 61 u = 0; 62 return true; 63 } 64 CSSM_SIZE len = cdata.Length; 65 if(len > sizeof(uint32)) { 66 return false; 67 } 68 69 uint32 rtn = 0; 70 uint8 *cp = cdata.Data; 71 for(uint32 i=0; i<len; i++) { 72 rtn = (rtn << 8) | *cp++; 73 } 74 u = rtn; 75 return true; 76} 77 78/* uint32 --> CSSM_DATA */ 79void p12IntToData( 80 uint32 num, 81 CSSM_DATA &cdata, 82 SecNssCoder &coder) 83{ 84 uint32 len = 0; 85 86 if(num < 0x100) { 87 len = 1; 88 } 89 else if(num < 0x10000) { 90 len = 2; 91 } 92 else if(num < 0x1000000) { 93 len = 3; 94 } 95 else { 96 len = 4; 97 } 98 coder.allocItem(cdata, len); 99 uint8 *cp = &cdata.Data[len - 1]; 100 for(unsigned i=0; i<len; i++) { 101 *cp-- = num & 0xff; 102 num >>= 8; 103 } 104} 105 106/* CFDataRef <--> CSSM_DATA */ 107CFDataRef p12CssmDataToCf( 108 const CSSM_DATA &c) 109{ 110 return CFDataCreate(NULL, c.Data, c.Length); 111} 112 113void p12CfDataToCssm( 114 CFDataRef cf, 115 CSSM_DATA &c, 116 SecNssCoder &coder) 117{ 118 coder.allocCopyItem(CFDataGetBytePtr(cf), 119 CFDataGetLength(cf), c); 120} 121 122/* 123 * Attempt to convert a CFStringRef, which represents a SafeBag's 124 * FriendlyName, to a UTF8-encoded CSSM_DATA. The CSSM_DATA and its 125 * referent are allocated in the specified SecNssCoder's memory. 126 * No guarantee that this conversion works. If it doesn't we return 127 * NULL and caller must be prepared to deal with that. 128 */ 129CSSM_DATA_PTR p12StringToUtf8( 130 CFStringRef cfStr, 131 SecNssCoder &coder) 132{ 133 if(cfStr == NULL) { 134 return NULL; 135 } 136 CFIndex strLen = CFStringGetLength(cfStr); 137 if(strLen == 0) { 138 return NULL; 139 } 140 CSSM_DATA_PTR rtn = coder.mallocn<CSSM_DATA>(); 141 coder.allocItem(*rtn, strLen + 1); 142 if(!CFStringGetCString(cfStr, (char *)rtn->Data,strLen + 1, 143 kCFStringEncodingUTF8)) { 144 /* not convertible from native Unicode to UTF8 */ 145 return NULL; 146 } 147 return rtn; 148} 149 150/* 151 * Enum to string mappper. 152 * Maybe DEBUG only. 153 */ 154/* 155 * Each type of attribute has a name/value pair in a table of these: 156 */ 157typedef struct { 158 unsigned value; 159 const char *name; 160} p12NameValuePair; 161 162/* declare one entry in a table of p12NameValuePair */ 163#define NVP(attr) {attr, #attr} 164 165/* the NULL entry which terminates all p12NameValuePair tables */ 166#define NVP_END {0, NULL} 167 168static const p12NameValuePair p7CITypeNames[] = 169{ 170 NVP(CT_None), 171 NVP(CT_Data), 172 NVP(CT_SignedData), 173 NVP(CT_EnvData), 174 NVP(CT_SignedEnvData), 175 NVP(CT_DigestData), 176 NVP(CT_EncryptedData), 177 NVP_END 178}; 179 180static const p12NameValuePair p12BagTypeNames[] = 181{ 182 NVP(BT_None), 183 NVP(BT_KeyBag), 184 NVP(BT_ShroudedKeyBag), 185 NVP(BT_CertBag), 186 NVP(BT_CrlBag), 187 NVP(BT_SecretBag), 188 NVP(BT_SafeContentsBag), 189 NVP_END 190}; 191 192static const char *typeToStr( 193 unsigned type, 194 const p12NameValuePair *table) 195{ 196 while(table->name) { 197 if(table->value == type) { 198 return table->name; 199 } 200 table++; 201 } 202 return "Unknown"; 203} 204 205const char *p12BagTypeStr( 206 NSS_P12_SB_Type type) 207{ 208 return typeToStr(type, p12BagTypeNames); 209} 210 211const char *p7ContentInfoTypeStr( 212 NSS_P7_CI_Type type) 213{ 214 return typeToStr(type, p7CITypeNames); 215} 216 217/* 218 * OIDS for P12 and PKCS5 v1.5 (PBES1) encrypt and decrypt map to the following 219 * attributes. 220 */ 221typedef struct { 222 const CSSM_OID *oid; 223 CSSM_ALGORITHMS keyAlg; // e.g., CSSM_ALGID_DES 224 CSSM_ALGORITHMS encrAlg; // e.g., CSSM_ALGID_3DES_3KEY_EDE 225 CSSM_ALGORITHMS pbeHashAlg; // SHA1 or MD5 226 uint32 keySizeInBits; 227 uint32 blockSizeInBytes; // for IV, optional 228 CSSM_PADDING padding; // CSSM_PADDING_PKCS7, etc. 229 CSSM_ENCRYPT_MODE mode; // CSSM_ALGMODE_CBCPadIV8, etc. 230 PKCS_Which pkcs; // PW_PKCS12 (for this module) or PW_PKCS5_v1_5 231} PKCSOidInfo; 232 233static const PKCSOidInfo pkcsOidInfos[] = { 234 /* PKCS12 first, the ones this module uses */ 235 { 236 &CSSMOID_PKCS12_pbeWithSHAAnd128BitRC4, 237 CSSM_ALGID_RC4, 238 CSSM_ALGID_RC4, 239 CSSM_ALGID_SHA1, 240 128, 241 0, // RC4 is a stream cipher 242 CSSM_PADDING_NONE, 243 CSSM_ALGMODE_NONE, 244 PW_PKCS12 245 }, 246 { 247 &CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4, 248 CSSM_ALGID_RC4, 249 CSSM_ALGID_RC4, 250 CSSM_ALGID_SHA1, 251 40, 252 0, // RC4 is a stream cipher 253 CSSM_PADDING_NONE, 254 CSSM_ALGMODE_NONE, 255 PW_PKCS12 256 }, 257 { 258 &CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC, 259 CSSM_ALGID_3DES_3KEY, 260 CSSM_ALGID_3DES_3KEY_EDE, 261 CSSM_ALGID_SHA1, 262 64 * 3, 263 8, 264 CSSM_PADDING_PKCS7, 265 CSSM_ALGMODE_CBCPadIV8, 266 PW_PKCS12 267 }, 268 { 269 &CSSMOID_PKCS12_pbeWithSHAAnd2Key3DESCBC, 270 CSSM_ALGID_3DES_2KEY, 271 CSSM_ALGID_3DES_2KEY_EDE, 272 CSSM_ALGID_SHA1, 273 64 * 2, 274 8, 275 CSSM_PADDING_PKCS7, 276 CSSM_ALGMODE_CBCPadIV8, 277 PW_PKCS12 278 }, 279 { 280 &CSSMOID_PKCS12_pbeWithSHAAnd128BitRC2CBC, 281 CSSM_ALGID_RC2, 282 CSSM_ALGID_RC2, 283 CSSM_ALGID_SHA1, 284 128, 285 8, 286 CSSM_PADDING_PKCS7, 287 CSSM_ALGMODE_CBCPadIV8, 288 PW_PKCS12 289 }, 290 { 291 &CSSMOID_PKCS12_pbewithSHAAnd40BitRC2CBC, 292 CSSM_ALGID_RC2, 293 CSSM_ALGID_RC2, 294 CSSM_ALGID_SHA1, 295 40, 296 8, 297 CSSM_PADDING_PKCS7, 298 CSSM_ALGMODE_CBCPadIV8, 299 PW_PKCS12 300 }, 301 302 /* PKCS5 v1.5, used for SecImportExport module */ 303 { 304 &CSSMOID_PKCS5_pbeWithMD2AndDES, 305 CSSM_ALGID_DES, 306 CSSM_ALGID_DES, 307 CSSM_ALGID_MD2, 308 64, 309 8, 310 CSSM_PADDING_PKCS7, 311 CSSM_ALGMODE_CBCPadIV8, 312 PW_PKCS5_v1_5 313 }, 314 { 315 &CSSMOID_PKCS5_pbeWithMD2AndRC2, 316 CSSM_ALGID_RC2, 317 CSSM_ALGID_RC2, 318 CSSM_ALGID_MD2, 319 64, 320 8, 321 CSSM_PADDING_PKCS7, 322 CSSM_ALGMODE_CBCPadIV8, 323 PW_PKCS5_v1_5 324 }, 325 { 326 &CSSMOID_PKCS5_pbeWithMD5AndDES, 327 CSSM_ALGID_DES, 328 CSSM_ALGID_DES, 329 CSSM_ALGID_MD5, 330 64, 331 8, 332 CSSM_PADDING_PKCS7, 333 CSSM_ALGMODE_CBCPadIV8, 334 PW_PKCS5_v1_5 335 }, 336 { 337 &CSSMOID_PKCS5_pbeWithMD5AndRC2, 338 CSSM_ALGID_RC2, 339 CSSM_ALGID_RC2, 340 CSSM_ALGID_MD5, 341 64, 342 8, 343 CSSM_PADDING_PKCS7, 344 CSSM_ALGMODE_CBCPadIV8, 345 PW_PKCS5_v1_5 346 }, 347 { 348 &CSSMOID_PKCS5_pbeWithSHA1AndDES, 349 CSSM_ALGID_DES, 350 CSSM_ALGID_DES, 351 CSSM_ALGID_SHA1, 352 64, 353 8, 354 CSSM_PADDING_PKCS7, 355 CSSM_ALGMODE_CBCPadIV8, 356 PW_PKCS5_v1_5 357 }, 358 { 359 &CSSMOID_PKCS5_pbeWithSHA1AndRC2, 360 CSSM_ALGID_RC2, 361 CSSM_ALGID_RC2, 362 CSSM_ALGID_SHA1, 363 64, 364 8, 365 CSSM_PADDING_PKCS7, 366 CSSM_ALGMODE_CBCPadIV8, 367 PW_PKCS5_v1_5 368 }, 369 370 /* finally one for PKCS5 v2.0, which has its own means of 371 * cooking up all the parameters */ 372 { 373 &CSSMOID_PKCS5_PBES2, 374 CSSM_ALGID_NONE, 375 CSSM_ALGID_NONE, 376 CSSM_ALGID_NONE, 377 0, 0, 0, 0, 378 PW_PKCS5_v2 379 } 380}; 381 382#define NUM_PKCS_OID_INFOS (sizeof(pkcsOidInfos) / sizeof(pkcsOidInfos[1])) 383 384/* map an OID to the components */ 385/* returns false if OID not found */ 386 387/* 388 * NOTE: as of March 8 2004 this is also used by the SecImportExport 389 * module...not just PKCS12! 390 */ 391bool pkcsOidToParams( 392 const CSSM_OID *oid, 393 CSSM_ALGORITHMS &keyAlg, // e.g., CSSM_ALGID_DES 394 CSSM_ALGORITHMS &encrAlg, // e.g., CSSM_ALGID_3DES_3KEY_EDE 395 CSSM_ALGORITHMS &pbeHashAlg, // SHA1 or MD5 396 uint32 &keySizeInBits, 397 uint32 &blockSizeInBytes, // for IV, optional 398 CSSM_PADDING &padding, // CSSM_PADDING_PKCS7, etc. 399 CSSM_ENCRYPT_MODE &mode, // CSSM_ALGMODE_CBCPadIV8, etc. 400 PKCS_Which &pkcs) // PW_PKCS5_v1_5 or PW_PKCS12 401{ 402 const PKCSOidInfo *info = pkcsOidInfos; 403 pkcs = PW_None; 404 405 for(unsigned dex=0; dex<NUM_PKCS_OID_INFOS; dex++) { 406 if(nssCompareCssmData(oid, info->oid)) { 407 keyAlg = info->keyAlg; 408 encrAlg = info->encrAlg; 409 pbeHashAlg = info->pbeHashAlg; 410 keySizeInBits = info->keySizeInBits; 411 blockSizeInBytes = info->blockSizeInBytes; 412 padding = info->padding; 413 mode = info->mode; 414 pkcs = info->pkcs; 415 return true; 416 } 417 info++; 418 } 419 return false; 420} 421 422/* 423 * Verify MAC on an existing PFX. 424 */ 425CSSM_RETURN p12VerifyMac( 426 const NSS_P12_DecodedPFX &pfx, 427 CSSM_CSP_HANDLE cspHand, 428 const CSSM_DATA *pwd, // unicode, double null terminated 429 const CSSM_KEY *passKey, 430 SecNssCoder &coder) // for temp mallocs 431{ 432 if(pfx.macData == NULL) { 433 return CSSMERR_CSP_INVALID_SIGNATURE; 434 } 435 NSS_P12_MacData &macData = *pfx.macData; 436 NSS_P7_DigestInfo &digestInfo = macData.mac; 437 CSSM_OID &algOid = digestInfo.digestAlgorithm.algorithm; 438 CSSM_ALGORITHMS macAlg; 439 if(!cssmOidToAlg(&algOid, &macAlg)) { 440 return CSSMERR_CSP_INVALID_ALGORITHM; 441 } 442 uint32 iterCount = 0; 443 CSSM_DATA &citer = macData.iterations; 444 if(!p12DataToInt(citer, iterCount)) { 445 return CSSMERR_CSP_INVALID_ATTR_ROUNDS; 446 } 447 if(iterCount == 0) { 448 /* optional, default 1 */ 449 iterCount = 1; 450 } 451 452 /* 453 * In classic fashion, the PKCS12 spec now says: 454 * 455 * When password integrity mode is used to secure a PFX PDU, 456 * an SHA-1 HMAC is computed on the BER-encoding of the contents 457 * of the content field of the authSafe field in the PFX PDU. 458 * 459 * So here we go. 460 */ 461 CSSM_DATA genMac; 462 CSSM_RETURN crtn = p12GenMac(cspHand, *pfx.authSafe.content.data, 463 macAlg, iterCount, macData.macSalt, pwd, passKey, coder, genMac); 464 if(crtn) { 465 return crtn; 466 } 467 if(nssCompareCssmData(&genMac, &digestInfo.digest)) { 468 return CSSM_OK; 469 } 470 else { 471 return CSSMERR_CSP_VERIFY_FAILED; 472 } 473} 474 475/* we generate 8 random bytes of salt */ 476#define P12_SALT_LEN 8 477 478void p12GenSalt( 479 CSSM_DATA &salt, 480 SecNssCoder &coder) 481{ 482 DevRandomGenerator rng; 483 coder.allocItem(salt, P12_SALT_LEN); 484 rng.random(salt.Data, P12_SALT_LEN); 485} 486 487/* 488 * Generate random label string to allow associating an imported private 489 * key with a cert. 490 */ 491void p12GenLabel( 492 CSSM_DATA &label, 493 SecNssCoder &coder) 494{ 495 /* first a random uint32 */ 496 uint8 d[4]; 497 DevRandomGenerator rng; 498 rng.random(d, 4); 499 CSSM_DATA cd = {4, d}; 500 uint32 i; 501 p12DataToInt(cd, i); 502 503 /* sprintf that into a real string */ 504 coder.allocItem(label, 9); 505 memset(label.Data, 0, 9); 506 sprintf((char *)label.Data, "%08X", (unsigned)i); 507} 508 509/* NULL algorithm parameters */ 510 511static const uint8 nullAlg[2] = {SEC_ASN1_NULL, 0}; 512 513void p12NullAlgParams( 514 CSSM_X509_ALGORITHM_IDENTIFIER &algId) 515{ 516 CSSM_DATA &p = algId.parameters; 517 p.Data = (uint8 *)nullAlg; 518 p.Length = 2; 519} 520 521/* 522 * Free memory via specified plugin's app-level allocator 523 */ 524void freeCssmMemory( 525 CSSM_HANDLE hand, 526 void *p) 527{ 528 CSSM_API_MEMORY_FUNCS memFuncs; 529 CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs); 530 if(crtn) { 531 p12LogCssmError("CSSM_GetAPIMemoryFunctions", crtn); 532 /* oh well, leak and continue */ 533 return; 534 } 535 memFuncs.free_func(p, memFuncs.AllocRef); 536} 537 538/* 539 * Find private key by label, modify its Label attr to be the 540 * hash of the associated public key. 541 * Also optionally re-sets the key's PrintName attribute; used to reset 542 * this attr from the random label we create when first unwrap it 543 * to the friendly name we find later after parsing attributes. 544 * Detection of a duplicate key when updating the key's attributes 545 * results in a lookup of the original key and returning it in 546 * foundKey. 547 */ 548CSSM_RETURN p12SetPubKeyHash( 549 CSSM_CSP_HANDLE cspHand, // where the key lives 550 CSSM_DL_DB_HANDLE dlDbHand, // ditto 551 CSSM_DATA &keyLabel, // for DB lookup 552 CSSM_DATA_PTR newPrintName, // optional 553 SecNssCoder &coder, // for mallocing newLabel 554 CSSM_DATA &newLabel, // RETURNED with label as hash 555 CSSM_KEY_PTR &foundKey) // RETURNED 556{ 557 CSSM_QUERY query; 558 CSSM_SELECTION_PREDICATE predicate; 559 CSSM_DB_UNIQUE_RECORD_PTR record = NULL; 560 CSSM_RETURN crtn; 561 CSSM_HANDLE resultHand = 0; 562 CSSM_DATA keyData = {0, NULL}; 563 CSSM_CC_HANDLE ccHand = 0; 564 CSSM_KEY_PTR privKey = NULL; 565 CSSM_DATA_PTR keyDigest = NULL; 566 567 assert(cspHand != 0); 568 query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; 569 query.Conjunctive = CSSM_DB_NONE; 570 query.NumSelectionPredicates = 1; 571 predicate.DbOperator = CSSM_DB_EQUAL; 572 573 predicate.Attribute.Info.AttributeNameFormat = 574 CSSM_DB_ATTRIBUTE_NAME_AS_STRING; 575 predicate.Attribute.Info.Label.AttributeName = 576 (char*) P12_KEY_ATTR_LABEL_AND_HASH; 577 predicate.Attribute.Info.AttributeFormat = 578 CSSM_DB_ATTRIBUTE_FORMAT_BLOB; 579 /* hope this cast is OK */ 580 predicate.Attribute.Value = &keyLabel; 581 query.SelectionPredicate = &predicate; 582 583 query.QueryLimits.TimeLimit = 0; // FIXME - meaningful? 584 query.QueryLimits.SizeLimit = 1; // FIXME - meaningful? 585 query.QueryFlags = CSSM_QUERY_RETURN_DATA; 586 587 /* build Record attribute with one or two attrs */ 588 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; 589 CSSM_DB_ATTRIBUTE_DATA attr[2]; 590 attr[0].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; 591 attr[0].Info.Label.AttributeName = (char*) P12_KEY_ATTR_LABEL_AND_HASH; 592 attr[0].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; 593 if(newPrintName) { 594 attr[1].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; 595 attr[1].Info.Label.AttributeName = (char*) P12_KEY_ATTR_PRINT_NAME; 596 attr[1].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; 597 } 598 recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; 599 recordAttrs.NumberOfAttributes = newPrintName ? 2 : 1; 600 recordAttrs.AttributeData = attr; 601 602 crtn = CSSM_DL_DataGetFirst(dlDbHand, 603 &query, 604 &resultHand, 605 &recordAttrs, 606 &keyData, // theData 607 &record); 608 /* abort only on success */ 609 if(crtn != CSSM_OK) { 610 p12LogCssmError("CSSM_DL_DataGetFirst", crtn); 611 p12ErrorLog("***p12SetPubKeyHash: can't find private key\n"); 612 return crtn; 613 } 614 /* subsequent errors to errOut: */ 615 if(keyData.Data == NULL) { 616 p12ErrorLog("***p12SetPubKeyHash: private key lookup failure\n"); 617 crtn = CSSMERR_CSSM_INTERNAL_ERROR; 618 goto errOut; 619 } 620 privKey = (CSSM_KEY_PTR)keyData.Data; 621 622 /* public key hash via passthrough - works on any key, any CSP/CSPDL.... */ 623 /* 624 * Warning! This relies on the current default ACL meaning "allow this 625 * current app to access this private key" since we created the key. 626 */ 627 crtn = CSSM_CSP_CreatePassThroughContext(cspHand, privKey, &ccHand); 628 if(crtn) { 629 p12LogCssmError("CSSM_CSP_CreatePassThroughContext", crtn); 630 goto errOut; 631 } 632 crtn = CSSM_CSP_PassThrough(ccHand, 633 CSSM_APPLECSP_KEYDIGEST, 634 NULL, 635 (void **)&keyDigest); 636 if(crtn) { 637 p12LogCssmError("CSSM_CSP_PassThrough", crtn); 638 goto errOut; 639 } 640 641 /* 642 * Replace Label attr data with hash. 643 * NOTE: the module which allocated this attribute data - a DL - 644 * was loaded and attached by out client layer, not by us. Thus 645 * we can't use the memory allocator functions *we* used when 646 * attaching to the CSP - we have to use the ones 647 * which the client registered with the DL. 648 */ 649 freeCssmMemory(dlDbHand.DLHandle, attr[0].Value->Data); 650 freeCssmMemory(dlDbHand.DLHandle, attr[0].Value); 651 if(newPrintName) { 652 freeCssmMemory(dlDbHand.DLHandle, attr[1].Value->Data); 653 freeCssmMemory(dlDbHand.DLHandle, attr[1].Value); 654 } 655 /* modify key attributes */ 656 attr[0].Value = keyDigest; 657 if(newPrintName) { 658 attr[1].Value = newPrintName; 659 } 660 crtn = CSSM_DL_DataModify(dlDbHand, 661 CSSM_DL_DB_RECORD_PRIVATE_KEY, 662 record, 663 &recordAttrs, 664 NULL, // DataToBeModified 665 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); 666 switch(crtn) { 667 case CSSM_OK: 668 /* give caller the key's new label */ 669 coder.allocCopyItem(*keyDigest, newLabel); 670 break; 671 default: 672 p12LogCssmError("CSSM_DL_DataModify", crtn); 673 break; 674 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA: 675 { 676 /* 677 * Special case: dup private key. The label we just tried to modify is 678 * the public key hash so we can be confident that this really is a dup. 679 * Delete it, look up the original, and return the original to caller. 680 */ 681 CSSM_RETURN drtn = CSSM_DL_DataDelete(dlDbHand, record); 682 if(drtn) { 683 p12LogCssmError("CSSM_DL_DataDelete on dup key", drtn); 684 crtn = drtn; 685 break; 686 } 687 688 /* Free items created in last search */ 689 CSSM_DL_DataAbortQuery(dlDbHand, resultHand); 690 resultHand = 0; 691 CSSM_DL_FreeUniqueRecord(dlDbHand, record); 692 record = NULL; 693 694 /* lookup by label as public key hash this time */ 695 predicate.Attribute.Value = keyDigest; 696 drtn = CSSM_DL_DataGetFirst(dlDbHand, 697 &query, 698 &resultHand, 699 NULL, // no attrs this time 700 &keyData, 701 &record); 702 if(drtn) { 703 p12LogCssmError("CSSM_DL_DataGetFirst on original key", crtn); 704 crtn = drtn; 705 break; 706 } 707 foundKey = (CSSM_KEY_PTR)keyData.Data; 708 /* give caller the key's actual label */ 709 coder.allocCopyItem(*keyDigest, newLabel); 710 break; 711 } 712 } 713 714errOut: 715 /* free resources */ 716 if(resultHand) { 717 CSSM_DL_DataAbortQuery(dlDbHand, resultHand); 718 } 719 if(record) { 720 CSSM_DL_FreeUniqueRecord(dlDbHand, record); 721 } 722 if(ccHand) { 723 CSSM_DeleteContext(ccHand); 724 } 725 if(privKey) { 726 /* key created by the CSPDL */ 727 CSSM_FreeKey(cspHand, NULL, privKey, CSSM_FALSE); 728 freeCssmMemory(dlDbHand.DLHandle, privKey); 729 } 730 if(keyDigest) { 731 /* mallocd by someone else's CSP */ 732 freeCssmMemory(cspHand, keyDigest->Data); 733 freeCssmMemory(cspHand, keyDigest); 734 } 735 return crtn; 736} 737 738/* 739 * Given a context specified via a CSSM_CC_HANDLE, add a new 740 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType, 741 * AttributeLength, and an untyped pointer. 742 */ 743CSSM_RETURN p12AddContextAttribute(CSSM_CC_HANDLE CCHandle, 744 uint32 AttributeType, 745 uint32 AttributeLength, 746 const void *AttributePtr) 747{ 748 CSSM_CONTEXT_ATTRIBUTE newAttr; 749 CSSM_RETURN crtn; 750 751 newAttr.AttributeType = AttributeType; 752 newAttr.AttributeLength = AttributeLength; 753 newAttr.Attribute.Data = (CSSM_DATA_PTR)AttributePtr; 754 crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr); 755 if(crtn) { 756 p12LogCssmError("CSSM_UpdateContextAttributes", crtn); 757 } 758 return crtn; 759} 760 761/* 762 * Find private key by specified label, delete it. 763 */ 764CSSM_RETURN p12DeleteKey( 765 CSSM_DL_DB_HANDLE dlDbHand, 766 const CSSM_DATA &keyLabel) 767{ 768 CSSM_QUERY query; 769 CSSM_SELECTION_PREDICATE predicate; 770 CSSM_DB_UNIQUE_RECORD_PTR record = NULL; 771 CSSM_RETURN crtn; 772 CSSM_HANDLE resultHand = 0; 773 774 query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; 775 query.Conjunctive = CSSM_DB_NONE; 776 query.NumSelectionPredicates = 1; 777 predicate.DbOperator = CSSM_DB_EQUAL; 778 779 predicate.Attribute.Info.AttributeNameFormat = 780 CSSM_DB_ATTRIBUTE_NAME_AS_STRING; 781 predicate.Attribute.Info.Label.AttributeName = 782 (char*) P12_KEY_ATTR_LABEL_AND_HASH; 783 predicate.Attribute.Info.AttributeFormat = 784 CSSM_DB_ATTRIBUTE_FORMAT_BLOB; 785 predicate.Attribute.Value = const_cast<CSSM_DATA_PTR>(&keyLabel); 786 787 query.SelectionPredicate = &predicate; 788 query.QueryLimits.TimeLimit = 0; 789 query.QueryLimits.SizeLimit = 1; 790 query.QueryFlags = 0; 791 792 crtn = CSSM_DL_DataGetFirst(dlDbHand, 793 &query, 794 &resultHand, 795 NULL, // attrs - don't need 'em 796 NULL, // theData - don't need it 797 &record); 798 /* abort only on success */ 799 if(crtn) { 800 p12LogCssmError("CSSM_DL_DataGetFirst", crtn); 801 p12ErrorLog("***p12DeleteKey: can't find private key\n"); 802 return crtn; 803 } 804 805 crtn = CSSM_DL_DataDelete(dlDbHand, record); 806 if(crtn) { 807 p12LogCssmError("CSSM_DL_DataDelete", crtn); 808 p12ErrorLog("***p12DeleteKey: can't delete private key\n"); 809 } 810 811 CSSM_DL_DataAbortQuery(dlDbHand, resultHand); 812 CSSM_DL_FreeUniqueRecord(dlDbHand, record); 813 return crtn; 814} 815 816/* convert App passphrase to array of chars used in P12 PBE */ 817void p12ImportPassPhrase( 818 CFStringRef inPhrase, 819 SecNssCoder &coder, 820 CSSM_DATA &outPhrase) 821{ 822 CFDataRef cfData = CFStringCreateExternalRepresentation(NULL, 823 inPhrase, kCFStringEncodingUTF8, 0); 824 if(cfData == NULL) { 825 p12ErrorLog("***p12ImportPassPhrase: can't convert passphrase to UTF8\n"); 826 MacOSError::throwMe(errSecParam); 827 } 828 CFIndex keyLen = CFDataGetLength(cfData); 829 coder.allocItem(outPhrase, keyLen); 830 memmove(outPhrase.Data, CFDataGetBytePtr(cfData), keyLen); 831 CFRelease(cfData); 832} 833