]> git.saurik.com Git - apple/security.git/blob - libsecurity_pkcs12/lib/pkcs12Encode.cpp
Security-55471.tar.gz
[apple/security.git] / libsecurity_pkcs12 / lib / pkcs12Encode.cpp
1 /*
2 * Copyright (c) 2003-2004 Apple Computer, 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
49 void 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
69 void 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 */
131 void 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 */
225 NSS_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 */
274 void 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 */
336 void 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
357 NSS_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
391 NSS_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
422 NSS_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
487 NSS_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 }