1/* 2 * Copyright (c) 2007-2010,2012-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#include <libDER/oids.h> 25#include <security_asn1/nssUtils.h> 26#include <security_asn1/SecAsn1Templates.h> 27#include <security_asn1/pkcs12Templates.h> 28 29#include <CommonCrypto/CommonCryptor.h> 30#include <CommonCrypto/CommonDigest.h> 31#include <CommonCrypto/CommonHMAC.h> 32 33#include <CoreFoundation/CoreFoundation.h> 34 35#include <AssertMacros.h> 36#include <Security/SecInternal.h> 37#include <utilities/debugging.h> 38 39#include "p12pbegen.h" 40#include "p12import.h" 41#include "SecImportExport.h" 42 43#ifdef NDEBUG 44#define p12DecodeLog(args...) 45#else 46#define p12DecodeLog(args...) secdebug("pkcs12", "%s\n", args) 47#endif 48 49int decode_item(pkcs12_context * context, const SecAsn1Item *item, 50 const SecAsn1Template *tmpl, void *dest); 51inline int decode_item(pkcs12_context * context, const SecAsn1Item *item, 52 const SecAsn1Template *tmpl, void *dest) 53{ 54 return SecAsn1Decode(context->coder, (const char *)item->Data, item->Length, tmpl, dest); 55} 56 57void alloc_item(pkcs12_context * context, SecAsn1Item *item, size_t len); 58inline void alloc_item(pkcs12_context * context, SecAsn1Item *item, size_t len) 59{ 60 SecAsn1AllocItem(context->coder, item, len); 61} 62 63/* 64 * OIDS for P12 map to the following attributes. 65 */ 66typedef struct { 67 CCAlgorithm alg; 68 uint32_t keySizeInBits; // XXX/cs make keysize in bytes 69 uint32_t blockSizeInBytes; // for IV, optional, make iv size in bytes 70 CCOptions options; // padding and mode. 71} PKCSOidInfo; 72 73/* PKCS12 algorithms OID_ISO_MEMBER, OID_US, OID_RSA, OID_PKCS, OID_PKCS_12 */ 74static const uint8_t PKCS12_pbep[] = { 42, 134, 72, 134, 247, 13, 1, 12, 1 }; 75static const DERItem OID_PKCS12_pbep = { (uint8_t*)PKCS12_pbep, sizeof(PKCS12_pbep) }; 76static const PKCSOidInfo pkcsOidInfos[] = { 77 { /*CSSMOID_PKCS12_pbeWithSHAAnd128BitRC4,*/ 78 kCCAlgorithmRC4, 128, 0/* stream cipher */, 0 }, 79 { /*CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4,*/ 80 kCCAlgorithmRC4, 40, 0/* stream cipher */, 0 }, 81 { /*CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC,*/ 82 kCCAlgorithm3DES, 64 * 3, 8, kCCOptionPKCS7Padding }, 83 { /*CSSMOID_PKCS12_pbeWithSHAAnd2Key3DESCBC,*/ 84 -1 /*CSSM_ALGID_3DES_2KEY unsupported*/, 64 * 2, 8, kCCOptionPKCS7Padding }, 85 { /*CSSMOID_PKCS12_pbeWithSHAAnd128BitRC2CBC,*/ 86 kCCAlgorithmRC2, 128, 8, kCCOptionPKCS7Padding }, 87 { /*CSSMOID_PKCS12_pbewithSHAAnd40BitRC2CBC,*/ 88 kCCAlgorithmRC2, 40, 8, kCCOptionPKCS7Padding } 89}; 90 91#define NUM_PKCS_OID_INFOS (sizeof(pkcsOidInfos) / sizeof(pkcsOidInfos[1])) 92 93static int pkcsOidToParams(const SecAsn1Item *oid, CCAlgorithm *alg, 94 uint32_t *keySizeInBits, uint32_t *blockSizeInBytes, CCOptions *options) 95{ 96 DERItem prefix = { oid->Data, oid->Length }; 97 prefix.length -= 1; 98 if (DEROidCompare(&OID_PKCS12_pbep, &prefix)) { 99 uint8_t postfix = oid->Data[oid->Length-1]; 100 if (postfix > NUM_PKCS_OID_INFOS || postfix == 4) 101 return -1; 102 *alg = pkcsOidInfos[postfix-1].alg; 103 *keySizeInBits = pkcsOidInfos[postfix-1].keySizeInBits; 104 *blockSizeInBytes = pkcsOidInfos[postfix-1].blockSizeInBytes; 105 *options = pkcsOidInfos[postfix-1].options; 106 return 0; 107 } 108 return -1; 109} 110 111static int p12DataToInt(const SecAsn1Item *cdata, uint32_t *u) 112{ 113 /* default/not present */ 114 if((cdata->Length == 0) || (cdata->Data == NULL)) { 115 *u = 0; 116 return 0; 117 } 118 size_t len = cdata->Length; 119 if(len > sizeof(uint32_t)) { 120 return -1; 121 } 122 123 uint32_t rtn = 0; 124 uint8_t *cp = cdata->Data; 125 size_t i; 126 for(i = 0; i < len; i++) { 127 rtn = (rtn << 8) | *cp++; 128 } 129 *u = rtn; 130 return 0; 131} 132 133/* 134 * Parse an SecAsn1AlgId specific to P12. 135 * Decode the alg params as a NSS_P12_PBE_Params and parse and 136 * return the result if the pbeParams is non-NULL. 137 */ 138static int algIdParse(pkcs12_context * context, 139 const SecAsn1AlgId *algId, NSS_P12_PBE_Params *pbeParams/*optional*/) 140{ 141 p12DecodeLog("algIdParse"); 142 const SecAsn1Item *param = &algId->parameters; 143 require(pbeParams, out); 144 require(param && param->Length, out); 145 memset(pbeParams, 0, sizeof(*pbeParams)); 146 require_noerr(decode_item(context, param, NSS_P12_PBE_ParamsTemplate, pbeParams), out); 147 148 return 0; 149out: 150 return -1; 151} 152 153static int p12Decrypt(pkcs12_context * context, const SecAsn1AlgId *algId, 154 const SecAsn1Item *cipherText, SecAsn1Item *plainText) 155{ 156 NSS_P12_PBE_Params pbep = {}; 157 // XXX/cs not requiring decoding, but if pbep is uninit this will fail later 158 algIdParse(context, algId, &pbep); 159 160 CCAlgorithm alg = 0; 161 uint32_t keySizeInBits = 0; 162 uint32_t blockSizeInBytes = 0; // for IV, optional 163 CCOptions options = 0; 164 require_noerr_quiet(pkcsOidToParams(&algId->algorithm, &alg, &keySizeInBits, 165 &blockSizeInBytes, &options), out); 166 167 uint32_t iterCount = 0; 168 require_noerr(p12DataToInt(&pbep.iterations, &iterCount), out); 169 170 /* P12 style key derivation */ 171 SecAsn1Item key = {0, NULL}; 172 if(keySizeInBits) 173 alloc_item(context, &key, (keySizeInBits+7)/8); 174 require_noerr(p12_pbe_gen(context->passphrase, pbep.salt.Data, pbep.salt.Length, 175 iterCount, PBE_ID_Key, key.Data, key.Length), out); 176 177 /* P12 style IV derivation, optional */ 178 SecAsn1Item iv = {0, NULL}; 179 if(blockSizeInBytes) { 180 alloc_item(context, &iv, blockSizeInBytes); 181 require_noerr(p12_pbe_gen(context->passphrase, pbep.salt.Data, pbep.salt.Length, 182 iterCount, PBE_ID_IV, iv.Data, iv.Length), out); 183 } 184 185 SecAsn1Item ourPtext = {0, NULL}; 186 alloc_item(context, &ourPtext, cipherText->Length); 187 require_noerr(CCCrypt(kCCDecrypt, alg, options/*kCCOptionPKCS7Padding*/, 188 key.Data, key.Length, iv.Data, cipherText->Data, cipherText->Length, 189 ourPtext.Data, ourPtext.Length, &ourPtext.Length), out); 190 *plainText = ourPtext; 191 192 return 0; 193out: 194 return -1; 195} 196 197static int emit_item(pkcs12_context * context, NSS_Attribute **attrs, 198 CFStringRef item_key, CFTypeRef item_value) 199{ 200 int result = -1; 201 /* parse attrs into friendlyName, localKeyId; ignoring generic attrs */ 202 CFMutableDictionaryRef attr_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 203 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 204 require(attr_dict, out); 205 unsigned numAttrs = nssArraySize((const void **)attrs); 206 unsigned int dex; 207 for(dex = 0; dex < numAttrs; dex++) { 208 NSS_Attribute *attr = attrs[dex]; 209 unsigned numValues = nssArraySize((const void**)attr->attrValue); 210 DERItem type = { attr->attrType.Data, attr->attrType.Length }; 211 if(DEROidCompare(&type, &oidFriendlyName)) { 212 /* 213 * BMP string (UniCode). Spec says only one legal value. 214 */ 215 require(numValues == 1, out); 216 SecAsn1Item friendly_name_asn1; 217 require_noerr(decode_item(context, attr->attrValue[0], 218 kSecAsn1BMPStringTemplate, &friendly_name_asn1), out); 219 CFStringRef friendly_name = CFStringCreateWithBytes(kCFAllocatorDefault, 220 friendly_name_asn1.Data, friendly_name_asn1.Length, 221 kCFStringEncodingUnicode, true); 222 if (friendly_name) { 223 CFDictionarySetValue(attr_dict, kSecImportItemLabel, friendly_name); 224 CFRelease(friendly_name); 225 } 226 } 227 else if(DEROidCompare(&type, &oidLocalKeyId)) { 228 /* 229 * Octet string. Spec says only one legal value. 230 */ 231 require(numValues == 1, out); 232 SecAsn1Item local_key_id; 233 require_noerr(decode_item(context, attr->attrValue[0], 234 kSecAsn1OctetStringTemplate, &local_key_id), out); 235 CFDataRef keyid = CFDataCreate(kCFAllocatorDefault, local_key_id.Data, local_key_id.Length); 236 if (keyid) { 237 CFDictionarySetValue(attr_dict, kSecImportItemKeyID, keyid); 238 CFRelease(keyid); 239 } 240 } 241 } 242 243 CFTypeRef key = CFDictionaryGetValue(attr_dict, kSecImportItemKeyID); 244 if (!key) 245 key = CFDictionaryGetValue(attr_dict, kSecImportItemLabel); 246 if (!key) 247 key = item_value; 248 249 CFMutableDictionaryRef item = (CFMutableDictionaryRef)CFDictionaryGetValue(context->items, key); 250 if (item) { 251 CFDictionarySetValue(item, item_key, item_value); 252 } else { 253 CFDictionarySetValue(attr_dict, item_key, item_value); 254 CFDictionarySetValue(context->items, key, attr_dict); 255 } 256 result = 0; 257out: 258 CFReleaseSafe(attr_dict); 259 return result; 260} 261 262 263/* 264 * ShroudedKeyBag parser w/decrypt 265 */ 266static int shroudedKeyBagParse(pkcs12_context * context, const NSS_P12_SafeBag *safeBag) 267{ 268 CFDataRef algoidData = NULL; 269 CFDataRef keyData = NULL; 270 271 p12DecodeLog("Found shrouded key bag"); 272 273 const NSS_P12_ShroudedKeyBag *keyBag = safeBag->bagValue.shroudedKeyBag; 274 SecAsn1Item ptext = {0, NULL}; 275 require_noerr_quiet(p12Decrypt(context, &keyBag->algorithm, 276 &keyBag->encryptedData, &ptext), out); 277 278 /* Decode PKCS#8 formatted private key */ 279 NSS_PrivateKeyInfo pki; 280 memset(&pki, 0, sizeof(pki)); 281 require_noerr(decode_item(context, &ptext, kSecAsn1PrivateKeyInfoTemplate, 282 &pki), out); 283 284 DERItem algorithm = { pki.algorithm.algorithm.Data, pki.algorithm.algorithm.Length }; 285 algoidData = NULL; 286 if (DEROidCompare(&oidEcPubKey, &algorithm)) { 287 algoidData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, oidEcPubKey.data, oidEcPubKey.length, kCFAllocatorNull); 288 } else if (DEROidCompare(&oidRsa, &algorithm)) { 289 algoidData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, oidRsa.data, oidRsa.length, kCFAllocatorNull); 290 } else { 291 goto out; 292 } 293 require_noerr(emit_item(context, safeBag->bagAttrs, CFSTR("algid"), algoidData), out); 294 CFReleaseNull(algoidData); 295 296 keyData = CFDataCreate(kCFAllocatorDefault, pki.privateKey.Data, pki.privateKey.Length); 297 require_noerr(emit_item(context, safeBag->bagAttrs, CFSTR("key"), keyData), out); 298 CFReleaseNull(keyData); 299 300 return 0; 301out: 302 CFReleaseSafe(algoidData); 303 CFReleaseSafe(keyData); 304 return -1; 305} 306 307 308/* 309 * CertBag parser 310 */ 311static int certBagParse(pkcs12_context * context, const NSS_P12_SafeBag *safeBag) 312{ 313 CFDataRef certData = NULL; 314 p12DecodeLog("found certBag"); 315 NSS_P12_CertBag *certBag = safeBag->bagValue.certBag; 316 317 switch(certBag->type) { 318 case CT_X509: 319 { 320 /* certType = CSSM_CERT_X_509v3; 321 certEncoding = CSSM_CERT_ENCODING_DER; */ 322 certData = CFDataCreate(kCFAllocatorDefault, certBag->certValue.Data, 323 certBag->certValue.Length); 324 require_noerr(emit_item(context, safeBag->bagAttrs, CFSTR("cert"), certData), out); 325 CFRelease(certData); 326 break; 327 } 328 case CT_SDSI: 329 /* certType = CSSM_CERT_SDSIv1; */ 330 /* it's base64 encoded - no value for that in this enum */ 331 break; 332 default: 333 return -1; 334 } 335 return 0; 336out: 337 CFReleaseSafe(certData); 338 return -1; 339} 340 341 342/* 343 * Parse an encoded NSS_P12_SafeContents. This could be either 344 * present as plaintext in an AuthSafe or decrypted. 345 */ 346static int safeContentsParse(pkcs12_context * context, const SecAsn1Item *contentsBlob) 347{ 348 p12DecodeLog("safeContentsParse"); 349 350 NSS_P12_SafeContents sc; 351 memset(&sc, 0, sizeof(sc)); 352 require_noerr(decode_item(context, contentsBlob, NSS_P12_SafeContentsTemplate, 353 &sc), out); 354 355 unsigned numBags = nssArraySize((const void **)sc.bags); 356 unsigned int dex; 357 for(dex=0; dex<numBags; dex++) { 358 NSS_P12_SafeBag *bag = sc.bags[dex]; 359 assert(bag != NULL); 360 361 /* ensure that *something* is there */ 362 require(bag->bagValue.keyBag != NULL, out); 363 364 /* 365 * Break out to individual bag type 366 */ 367 switch(bag->type) { 368 case BT_ShroudedKeyBag: 369 require_noerr(shroudedKeyBagParse(context, bag), out); 370 break; 371 case BT_CertBag: 372 require_noerr(certBagParse(context, bag), out); 373 break; 374 375 case BT_KeyBag: 376 /* keyBagParse(bag); */ 377 p12DecodeLog("Unhandled BT_KeyBag"); 378 break; 379 case BT_CrlBag: 380 /* crlBagParse(bag); */ 381 p12DecodeLog("Unhandled BT_CrlBag"); 382 break; 383 case BT_SecretBag: 384 /* secretBagParse(bag); */ 385 p12DecodeLog("Unhandled BT_SecretBag"); 386 break; 387 case BT_SafeContentsBag: 388 /* safeContentsBagParse(bag); */ 389 p12DecodeLog("Unhandled BT_SafeContentsBag"); 390 break; 391 default: 392 p12DecodeLog("Unknown bag type"); 393 goto out; 394 break; 395 } 396 } 397 return 0; 398out: 399 return -1; 400} 401 402/* 403 * Parse a ContentInfo in the context of (i.e., as an element of) 404 * an AuthenticatedSafe. 405 */ 406static int authSafeElementParse(pkcs12_context * context, const NSS_P7_DecodedContentInfo *info) 407{ 408 p12DecodeLog("authSafeElementParse"); 409 switch(info->type) { 410 case CT_Data: 411 /* unencrypted SafeContents */ 412 require_noerr(safeContentsParse(context, info->content.data), out); 413 break; 414 415 case CT_EncryptedData: 416 { 417 /* 418 * Decrypt contents to get a SafeContents and 419 * then parse that. 420 */ 421 SecAsn1Item ptext = {0, NULL}; 422 NSS_P7_EncryptedData *edata = info->content.encryptData; 423 require_noerr_quiet(p12Decrypt(context, &edata->contentInfo.encrAlg, 424 &edata->contentInfo.encrContent, &ptext), out); 425 require_noerr(safeContentsParse(context, &ptext), out); 426 break; 427 } 428 default: 429 break; 430 } 431 return 0; 432out: 433 return -1; 434} 435 436/* 437 * Parse an encoded NSS_P12_AuthenticatedSafe 438 */ 439static int authSafeParse(pkcs12_context * context, const SecAsn1Item *authSafeBlob) 440{ 441 p12DecodeLog("authSafeParse"); 442 NSS_P12_AuthenticatedSafe authSafe; 443 memset(&authSafe, 0, sizeof(authSafe)); 444 require_noerr(decode_item(context, authSafeBlob, 445 NSS_P12_AuthenticatedSafeTemplate, &authSafe), out); 446 447 unsigned numInfos = nssArraySize((const void **)authSafe.info); 448 unsigned int dex; 449 for (dex=0; dex<numInfos; dex++) { 450 NSS_P7_DecodedContentInfo *info = authSafe.info[dex]; 451 require_noerr_quiet(authSafeElementParse(context, info), out); 452 } 453 return 0; 454out: 455 return -1; 456} 457 458static int p12VerifyMac(pkcs12_context * context, const NSS_P12_DecodedPFX *pfx) 459{ 460 NSS_P12_MacData *macData = pfx->macData; 461 require(macData, out); 462 NSS_P7_DigestInfo *digestInfo = &macData->mac; 463 require(digestInfo, out); 464 SecAsn1Item *algOid = &digestInfo->digestAlgorithm.algorithm; 465 require(algOid, out); 466 467 /* has to be OID_OIW_SHA1 */ 468 DERItem algOidItem = { algOid->Data, algOid->Length }; 469 require(algOidItem.length && DEROidCompare(&oidSha1, &algOidItem), out); 470 471 uint32_t iterCount = 0; 472 require_noerr_quiet(p12DataToInt(&macData->iterations, &iterCount), out); 473 if (iterCount == 0) { /* optional, default 1 */ 474 iterCount = 1; 475 } 476 477 /* 478 * In classic fashion, the PKCS12 spec now says: 479 * 480 * When password integrity mode is used to secure a PFX PDU, 481 * an SHA-1 HMAC is computed on the BER-encoding of the contents 482 * of the content field of the authSafe field in the PFX PDU. 483 * 484 * So here we go. 485 */ 486 uint8_t hmac_key[CC_SHA1_DIGEST_LENGTH]; 487 require_noerr_quiet(p12_pbe_gen(context->passphrase, 488 macData->macSalt.Data, macData->macSalt.Length, 489 iterCount, PBE_ID_MAC, hmac_key, sizeof(hmac_key)), out); 490 491 /* prealloc the mac data */ 492 SecAsn1Item verifyMac; 493 alloc_item(context, &verifyMac, CC_SHA1_DIGEST_LENGTH); 494 SecAsn1Item *ptext = pfx->authSafe.content.data; 495 CCHmac(kCCHmacAlgSHA1, hmac_key, CC_SHA1_DIGEST_LENGTH, 496 ptext->Data, ptext->Length, verifyMac.Data); 497 require_quiet(nssCompareSecAsn1Items(&verifyMac, &digestInfo->digest), out); 498 499 return 0; 500out: 501 return -1; 502} 503 504p12_error p12decode(pkcs12_context * context, CFDataRef cdpfx) 505{ 506 int err = p12_decodeErr; 507 NSS_P12_DecodedPFX pfx; 508 memset(&pfx, 0, sizeof(pfx)); 509 SecAsn1Item raw_blob = { CFDataGetLength(cdpfx), (void*)CFDataGetBytePtr(cdpfx) }; 510 511 require_noerr_quiet(decode_item(context, &raw_blob, NSS_P12_DecodedPFXTemplate, &pfx), out); 512 NSS_P7_DecodedContentInfo *dci = &pfx.authSafe; 513 514 /* only support CT_Data at top level (password based integrity mode) */ 515 require(dci->type == CT_Data, out); 516 require(pfx.macData, out); 517 518 require_noerr_action_quiet(p12VerifyMac(context, &pfx), out, err = p12_passwordErr); 519 require_noerr_quiet(authSafeParse(context, dci->content.data), out); 520 521 return errSecSuccess; 522out: 523 return err; 524} 525