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