/ OSX / libsecurity_pkcs12 / lib / pkcs12Encode.cpp
pkcs12Encode.cpp
  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  
 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  }