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 * pkcs12Encode.h - P12Coder encoding engine. 26 * 27 * Unlike the decoding side of P12Coder, which can parse PFXs with 28 * more or less arbitrary structures, this encoding engine has 29 * a specific layout for what bags go where in the jungle of 30 * SafeContents and ContentInfos. It would be impractical to allow 31 * (or to expect) the app to specify this structure. 32 * 33 * The knowledge of how a PFX is built out of various components 34 * is encapsulated in the authSafeBuild() member function. The rest 35 * of the functions in this file are pretty much "PFX-structure- 36 * agnostic", so if one wanted to change the overall PFX structure, 37 * one would only have to focus on the authSafeBuild() function. 38 */ 39 40#include "pkcs12Coder.h" 41#include "pkcs12Debug.h" 42#include "pkcs12Crypto.h" 43#include "pkcs12Templates.h" 44#include "pkcs12Utils.h" 45#include <Security/cssmerr.h> 46#include <Security/oidsattr.h> 47#include <Security/SecBase.h> 48 49void P12Coder::encode( 50 CFDataRef *cpfx) // RETURNED 51{ 52 p12EncodeLog("encode top"); 53 SecNssCoder localCdr; 54 NSS_P12_DecodedPFX pfx; 55 56 memset(&pfx, 0, sizeof(pfx)); 57 p12IntToData(3, pfx.version, localCdr); 58 authSafeBuild(pfx.authSafe, localCdr); 59 macSignPfx(pfx, localCdr); 60 CSSM_DATA derPfx = {0, NULL}; 61 if(localCdr.encodeItem(&pfx, NSS_P12_DecodedPFXTemplate, derPfx)) { 62 p12ErrorLog("Error encoding top-level pfx\n"); 63 P12_THROW_ENCODE; 64 } 65 CFDataRef cp = CFDataCreate(NULL, derPfx.Data, derPfx.Length); 66 *cpfx = cp; 67} 68 69void P12Coder::macSignPfx( 70 NSS_P12_DecodedPFX &pfx, 71 SecNssCoder &localCdr) 72{ 73 p12EncodeLog("macSignPfx"); 74 NSS_P12_MacData *macData = localCdr.mallocn<NSS_P12_MacData>(); 75 pfx.macData = macData; 76 p12GenSalt(macData->macSalt, localCdr); 77 p12IntToData(mMacIterCount, macData->iterations, localCdr); 78 NSS_P7_DigestInfo &digInfo = macData->mac; 79 80 /* this is not negotiable; it's the only one P12 allows */ 81 localCdr.allocCopyItem(CSSMOID_SHA1, digInfo.digestAlgorithm.algorithm); 82 /* null algorithm parameters */ 83 p12NullAlgParams(digInfo.digestAlgorithm); 84 85 const CSSM_DATA *macPhrase = getMacPassPhrase(); 86 const CSSM_KEY *macPassKey = getMacPassKey(); 87 if((macPhrase == NULL) && (macPassKey == NULL)) { 88 p12ErrorLog("no passphrase set\n"); 89 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE); 90 } 91 92 CSSM_RETURN crtn = p12GenMac(mCspHand, 93 *pfx.authSafe.content.data, 94 CSSM_ALGID_SHA1, mMacIterCount, macData->macSalt, 95 macPhrase, macPassKey, localCdr, digInfo.digest); 96 if(crtn) { 97 p12ErrorLog("Error generating PFX MAC\n"); 98 CssmError::throwMe(crtn); 99 } 100} 101 102/* 103 * This is the heart of the encoding engine. All knowledge of 104 * "what bags go where" is here. The PFX structure implemented here 105 * is derived from empirical observation of PFXs obtained from 106 * Mozilla 1.2b and from the DoD test vectors for "Conformance 107 * Testing of Relying Party Client Certificate Path Processing 108 * Logic", written by Cygnacom, Septemtber 28, 2001. 109 * 110 * The PFX structure is as follows: 111 * 112 * -- One AuthenticatedSafe element (a PKCS7 ContentInfo) containing 113 * all certificates and CRLs. 114 * 115 * ContentInfo.type = CT_EncryptedData 116 * Encryption algorithm is our "weak" encryption Alg, default 117 * of CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4. 118 * 119 * -- One AuthenticatedSafe element containing all private keys in 120 * the form of ShroudedKeyBags. 121 * 122 * ContentInfo.type = CT_Data 123 * Encryption algorithm for shrouded key bags is our "strong" 124 * encryption, default CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC 125 * 126 * -- Everything else goes in another AuthenticatedSafe element. 127 * 128 * ContentInfo.type = CT_EncryptedData 129 * Encryption algorithm is our "strong" encryption Alg 130 */ 131void P12Coder::authSafeBuild( 132 NSS_P7_DecodedContentInfo &authSafe, 133 SecNssCoder &localCdr) 134{ 135 p12EncodeLog("authSafeBuild top"); 136 137 /* how many contentInfos are we going to build? */ 138 unsigned numContents = 0; 139 if(mCerts.size() || mCrls.size()) { 140 numContents++; 141 } 142 if(mKeys.size()) { 143 numContents++; 144 } 145 if(mOpaques.size()) { 146 numContents++; 147 } 148 149 if(numContents == 0) { 150 p12ErrorLog("authSafeBuild: no contents\n"); 151 MacOSError::throwMe(errSecParam); 152 } 153 154 NSS_P7_DecodedContentInfo **contents = 155 (NSS_P7_DecodedContentInfo **)p12NssNullArray(numContents, 156 localCdr); 157 unsigned contentDex = 0; 158 159 NSS_P12_SafeBag **safeBags; 160 161 /* certs & crls */ 162 unsigned numBags = (unsigned)(mCerts.size() + mCrls.size()); 163 p12EncodeLog("authSafeBuild : %u certs + CRLS", numBags); 164 if(numBags) { 165 safeBags = (NSS_P12_SafeBag **)p12NssNullArray(numBags, localCdr); 166 unsigned bagDex = 0; 167 for(unsigned dex=0; dex<mCerts.size(); dex++) { 168 safeBags[bagDex++] = certBagBuild(mCerts[dex], localCdr); 169 } 170 for(unsigned dex=0; dex<mCrls.size(); dex++) { 171 safeBags[bagDex++] = crlBagBuild(mCrls[dex], localCdr); 172 } 173 contents[contentDex++] = safeContentsBuild(safeBags, 174 CT_EncryptedData, &mWeakEncrAlg, mWeakEncrIterCount, localCdr); 175 } 176 177 /* shrouded keys - encrypted at bag level */ 178 numBags = (unsigned)mKeys.size(); 179 if(numBags) { 180 p12EncodeLog("authSafeBuild : %u keys", numBags); 181 safeBags = (NSS_P12_SafeBag **)p12NssNullArray(numBags, localCdr); 182 unsigned bagDex = 0; 183 for(unsigned dex=0; dex<numBags; dex++) { 184 safeBags[bagDex++] = keyBagBuild(mKeys[dex], localCdr); 185 } 186 contents[contentDex++] = safeContentsBuild(safeBags, 187 CT_Data, NULL, 0, localCdr); 188 } 189 190 /* opaque */ 191 numBags = (unsigned)mOpaques.size(); 192 if(numBags) { 193 p12EncodeLog("authSafeBuild : %u opaques", numBags); 194 safeBags = (NSS_P12_SafeBag **)p12NssNullArray(numBags, localCdr); 195 unsigned bagDex = 0; 196 for(unsigned dex=0; dex<numBags; dex++) { 197 safeBags[bagDex++] = opaqueBagBuild(mOpaques[dex], localCdr); 198 } 199 contents[contentDex++] = safeContentsBuild(safeBags, 200 CT_EncryptedData, &mStrongEncrAlg, mStrongEncrIterCount, localCdr); 201 } 202 203 /* 204 * Encode the whole elements array into authSafe.content.data 205 */ 206 NSS_P12_AuthenticatedSafe safe; 207 safe.info = contents; 208 CSSM_DATA *adata = localCdr.mallocn<CSSM_DATA>(); 209 authSafe.content.data = adata; 210 adata->Data = NULL; 211 adata->Length = 0; 212 if(localCdr.encodeItem(&safe, NSS_P12_AuthenticatedSafeTemplate, 213 *adata)) { 214 p12ErrorLog("authSafeBuild: error encoding auth safe\n"); 215 P12_THROW_ENCODE; 216 } 217 authSafe.type = CT_Data; 218 authSafe.contentType = CSSMOID_PKCS7_Data; 219} 220 221/* 222 * Build a AuthSafe element of specified type out of the 223 * specified array of bags. 224 */ 225NSS_P7_DecodedContentInfo *P12Coder::safeContentsBuild( 226 NSS_P12_SafeBag **bags, 227 NSS_P7_CI_Type type, // CT_Data, CT_EncryptedData 228 CSSM_OID *encrOid, // only for CT_EncryptedData 229 unsigned iterCount, // ditto 230 SecNssCoder &localCdr) 231{ 232 p12EncodeLog("safeContentsBuild type %u", (unsigned)type); 233 234 /* 235 * First, encode the bag array as a SafeContents 236 */ 237 CSSM_DATA encSafeContents = {0, NULL}; 238 NSS_P12_SafeContents safeContents = {bags}; 239 if(localCdr.encodeItem(&safeContents, 240 NSS_P12_SafeContentsTemplate, encSafeContents)) { 241 p12ErrorLog("error encoding SafeContents\n"); 242 P12_THROW_ENCODE; 243 } 244 245 NSS_P7_DecodedContentInfo *dci = 246 localCdr.mallocn<NSS_P7_DecodedContentInfo>(); 247 dci->type = type; 248 if(type == CT_Data) { 249 /* plaintext gets encoded as an octet string */ 250 localCdr.allocCopyItem(CSSMOID_PKCS7_Data, dci->contentType); 251 dci->content.data = localCdr.mallocn<CSSM_DATA>(); 252 localCdr.allocCopyItem(encSafeContents, *dci->content.data); 253 } 254 else if(type == CT_EncryptedData) { 255 /* encrypt the encoded SafeContents */ 256 localCdr.allocCopyItem(CSSMOID_PKCS7_EncryptedData, 257 dci->contentType); 258 dci->content.encryptData = localCdr.mallocn<NSS_P7_EncryptedData>(); 259 NSS_P7_EncryptedData *ed = dci->content.encryptData; 260 assert(encrOid != NULL); 261 encryptData(encSafeContents, *encrOid, iterCount, *ed, localCdr); 262 } 263 else { 264 p12ErrorLog("bad type in safeContentsBuild\n"); 265 CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR); 266 } 267 return dci; 268} 269 270/* 271 * Encrypt the specified plaintext with specified algorithm. 272 * Drop result and other interesting info into an NSS_P7_EncryptedData. 273 */ 274void P12Coder::encryptData( 275 const CSSM_DATA &ptext, 276 CSSM_OID &encrOid, 277 unsigned iterCount, 278 NSS_P7_EncryptedData &ed, 279 SecNssCoder &localCdr) 280{ 281 p12EncodeLog("encryptData"); 282 283 /* do the raw encrypt first to make sure we can do it... */ 284 CSSM_ALGORITHMS keyAlg; // e.g., CSSM_ALGID_DES 285 CSSM_ALGORITHMS encrAlg; // e.g., CSSM_ALGID_3DES_3KEY_EDE 286 CSSM_ALGORITHMS pbeHashAlg; // SHA1 or MD5 287 uint32 keySizeInBits; 288 uint32 blockSizeInBytes; // for IV, optional 289 CSSM_PADDING padding; // CSSM_PADDING_PKCS7, etc. 290 CSSM_ENCRYPT_MODE mode; // CSSM_ALGMODE_CBCPadIV8, etc. 291 PKCS_Which pkcs; 292 293 bool found = pkcsOidToParams(&encrOid, 294 keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes, 295 padding, mode, pkcs); 296 if(!found || (pkcs != PW_PKCS12)) { 297 p12ErrorLog("encryptData encrAlg not understood\n"); 298 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); 299 } 300 301 /* Salt: we generate random bytes */ 302 CSSM_DATA salt; 303 p12GenSalt(salt, localCdr); 304 305 const CSSM_DATA *pwd = getEncrPassPhrase(); 306 const CSSM_KEY *passKey = getEncrPassKey(); 307 if((pwd == NULL) && (passKey == NULL)) { 308 p12ErrorLog("no passphrase set\n"); 309 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE); 310 } 311 CSSM_DATA ctext = {0, NULL}; 312 313 CSSM_RETURN crtn = p12Encrypt(mCspHand, ptext, 314 keyAlg, encrAlg, pbeHashAlg, 315 keySizeInBits, blockSizeInBytes, 316 padding, mode, 317 iterCount, salt, 318 pwd, passKey, localCdr, 319 ctext); 320 if(crtn) { 321 CssmError::throwMe(crtn); 322 } 323 324 /* Now fill in the NSS_P7_EncryptedData */ 325 p12IntToData(0, ed.version, localCdr); 326 NSS_P7_EncrContentInfo &eci = ed.contentInfo; 327 localCdr.allocCopyItem(CSSMOID_PKCS7_Data, eci.contentType); 328 algIdBuild(eci.encrAlg, encrOid, salt, iterCount, localCdr); 329 eci.encrContent = ctext; 330} 331 332/* 333 * Fill in an CSSM_X509_ALGORITHM_IDENTIFIER with parameters in 334 * the form of an encoded NSS_P12_PBE_Params 335 */ 336void P12Coder::algIdBuild( 337 CSSM_X509_ALGORITHM_IDENTIFIER &algId, 338 const CSSM_OID &algOid, 339 const CSSM_DATA &salt, 340 unsigned iterCount, 341 SecNssCoder &localCdr) 342{ 343 p12EncodeLog("algIdBuild"); 344 localCdr.allocCopyItem(algOid, algId.algorithm); 345 NSS_P12_PBE_Params pbeParams; 346 pbeParams.salt = salt; 347 p12IntToData(iterCount, pbeParams.iterations, localCdr); 348 if(localCdr.encodeItem(&pbeParams, NSS_P12_PBE_ParamsTemplate, 349 algId.parameters)) { 350 p12ErrorLog("error encoding NSS_P12_PBE_Params\n"); 351 P12_THROW_ENCODE; 352 } 353} 354 355#pragma mark --- Individual Bag Builders --- 356 357NSS_P12_SafeBag *P12Coder::certBagBuild( 358 P12CertBag *cert, 359 SecNssCoder &localCdr) 360{ 361 p12EncodeLog("certBagBuild"); 362 363 NSS_P12_SafeBag *safeBag = localCdr.mallocn<NSS_P12_SafeBag>(); 364 safeBag->bagId = CSSMOID_PKCS12_certBag; 365 safeBag->type = BT_CertBag; 366 367 NSS_P12_CertBag *certBag = localCdr.mallocn<NSS_P12_CertBag>(); 368 safeBag->bagValue.certBag = certBag; 369 const CSSM_OID *certTypeOid = NULL; 370 switch(cert->certType()) { 371 case CT_X509: 372 certTypeOid = &CSSMOID_PKCS9_X509Certificate; 373 break; 374 case CT_SDSI: 375 certTypeOid = &CSSMOID_PKCS9_SdsiCertificate; 376 break; 377 default: 378 p12ErrorLog("unknown certType on encode\n"); 379 CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR); 380 381 } 382 383 /* copies not needed, same scope as P12CertBag */ 384 certBag->bagType = *certTypeOid; 385 certBag->type = cert->certType(); 386 certBag->certValue = cert->certData(); 387 safeBag->bagAttrs = cert->getAllAttrs(); 388 return safeBag; 389} 390 391NSS_P12_SafeBag *P12Coder::crlBagBuild( 392 P12CrlBag *crl, 393 SecNssCoder &localCdr) 394{ 395 p12EncodeLog("crlBagBuild"); 396 397 NSS_P12_SafeBag *safeBag = localCdr.mallocn<NSS_P12_SafeBag>(); 398 safeBag->bagId = CSSMOID_PKCS12_crlBag; 399 safeBag->type = BT_CrlBag; 400 401 NSS_P12_CrlBag *crlBag = localCdr.mallocn<NSS_P12_CrlBag>(); 402 safeBag->bagValue.crlBag = crlBag; 403 const CSSM_OID *crlTypeOid = NULL; 404 switch(crl->crlType()) { 405 case CRT_X509: 406 crlTypeOid = &CSSMOID_PKCS9_X509Crl; 407 break; 408 default: 409 p12ErrorLog("unknown crlType on encode\n"); 410 CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR); 411 412 } 413 414 /* copies not needed, same scope as P12CrlBag */ 415 crlBag->bagType = *crlTypeOid; 416 crlBag->type = crl->crlType(); 417 crlBag->crlValue = crl->crlData(); 418 safeBag->bagAttrs = crl->getAllAttrs(); 419 return safeBag; 420} 421 422NSS_P12_SafeBag *P12Coder::keyBagBuild( 423 P12KeyBag *key, 424 SecNssCoder &localCdr) 425{ 426 p12EncodeLog("keyBagBuild"); 427 428 NSS_P12_SafeBag *safeBag = localCdr.mallocn<NSS_P12_SafeBag>(); 429 safeBag->bagId = CSSMOID_PKCS12_shroudedKeyBag; 430 safeBag->type = BT_ShroudedKeyBag; 431 432 NSS_EncryptedPrivateKeyInfo *keyInfo = localCdr. 433 mallocn<NSS_EncryptedPrivateKeyInfo>(); 434 safeBag->bagValue.shroudedKeyBag = keyInfo; 435 safeBag->bagAttrs = key->getAllAttrs(); 436 437 /* Prepare for key wrap */ 438 CSSM_DATA salt; 439 p12GenSalt(salt, localCdr); 440 algIdBuild(keyInfo->algorithm, mStrongEncrAlg, salt, 441 mStrongEncrIterCount, localCdr); 442 443 CSSM_ALGORITHMS keyAlg; // e.g., CSSM_ALGID_DES 444 CSSM_ALGORITHMS encrAlg; // e.g., CSSM_ALGID_3DES_3KEY_EDE 445 CSSM_ALGORITHMS pbeHashAlg; // SHA1 or MD5 446 uint32 keySizeInBits; 447 uint32 blockSizeInBytes; // for IV, optional 448 CSSM_PADDING padding; // CSSM_PADDING_PKCS7, etc. 449 CSSM_ENCRYPT_MODE mode; // CSSM_ALGMODE_CBCPadIV8, etc. 450 PKCS_Which pkcs; 451 452 bool found = pkcsOidToParams(&mStrongEncrAlg, 453 keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes, 454 padding, mode, pkcs); 455 if(!found || (pkcs != PW_PKCS12)) { 456 /* app config error - they gave us bogus algorithm */ 457 p12ErrorLog("keyBagBuild encrAlg not understood\n"); 458 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); 459 } 460 const CSSM_DATA *encrPhrase = getEncrPassPhrase(); 461 const CSSM_KEY *passKey = getEncrPassKey(); 462 if((encrPhrase == NULL) && (passKey == NULL)) { 463 p12ErrorLog("no passphrase set\n"); 464 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE); 465 } 466 CSSM_DATA shroudedBits = {0, NULL}; 467 468 CSSM_RETURN crtn = p12WrapKey(mCspHand, 469 key->key(), key->privKeyCreds(), 470 keyAlg, encrAlg, pbeHashAlg, 471 keySizeInBits, blockSizeInBytes, 472 padding, mode, 473 mStrongEncrIterCount, salt, 474 encrPhrase, 475 passKey, 476 localCdr, 477 shroudedBits); 478 if(crtn) { 479 p12ErrorLog("Error wrapping private key\n"); 480 CssmError::throwMe(crtn); 481 } 482 483 keyInfo->encryptedData = shroudedBits; 484 return safeBag; 485} 486 487NSS_P12_SafeBag *P12Coder::opaqueBagBuild( 488 P12OpaqueBag *opaque, 489 SecNssCoder &localCdr) 490{ 491 p12EncodeLog("opaqueBagBuild"); 492 NSS_P12_SafeBag *safeBag = localCdr.mallocn<NSS_P12_SafeBag>(); 493 safeBag->bagId = opaque->oid(); 494 safeBag->bagValue.secretBag = &opaque->blob(); 495 safeBag->bagAttrs = opaque->getAllAttrs(); 496 return safeBag; 497} 498