1/*
2 * grunt-quality p12 parse tool.
3 *
4 * The PFX ripper in this file uses, and always will use, the
5 * app-space reference PBE and crypto routines in p12Crypto.{h,cpp}
6 * and p12pbe.{h,cpp} in this directory.
7 */
8#include "p12Parse.h"
9#include "p12Crypto.h"
10#include "pkcs12Parsed.h"
11#include "pkcs12Utils.h"
12#include <Security/asn1Templates.h>
13#include <security_asn1/nssUtils.h>
14#include <security_asn1/SecNssCoder.h>
15#include <security_cdsa_utils/cuOidParser.h>
16#include <security_cdsa_utils/cuPrintCert.h>
17#include <security_pkcs12/pkcs7Templates.h>
18#include <security_pkcs12/pkcs12Templates.h>
19#include <security_cdsa_utils/cuFileIo.h>
20#include <stdlib.h>
21#include <stdio.h>
22#include <string.h>
23#include <CoreFoundation/CoreFoundation.h>
24#include <security_cdsa_utils/cuCdsaUtils.h>
25#include <Security/oidsattr.h>
26#include <stdexcept>
27
28/*
29 * The stuff which gets passed around to all parse modules
30 */
31class P12ParseInfo
32{
33public:
34	P12ParseInfo(SecNssCoder &coder,
35		CSSM_CSP_HANDLE cspHand,
36		OidParser &parser,
37		/* NULL means don't verify MAC, don't decrypt */
38		CFStringRef macPwd,
39		/* if this second pwd is absent, use macPwd for both */
40		CFStringRef encrPwd,
41		P12Parsed &parsed)			// destination
42			: mCoder(coder),
43			mCspHand(cspHand),
44			mParser(parser),
45			mParsed(parsed)
46		{
47			importPwd(macPwd, mPwd);
48			importPwd(encrPwd, mEncrPwd);
49		}
50
51	~P12ParseInfo() {}
52
53	void importPwd(CFStringRef str, CSSM_DATA &pwd);
54
55	SecNssCoder			&mCoder;
56	CSSM_CSP_HANDLE 	mCspHand;
57	OidParser 			&mParser;
58	CSSM_DATA			mPwd;		// unicode, double null terminated
59	CSSM_DATA			mEncrPwd;
60	P12Parsed 			&mParsed;	// destination
61
62};
63
64void P12ParseInfo::importPwd(
65	CFStringRef str,
66	CSSM_DATA &pwd)
67{
68	if(str == NULL) {
69		pwd.Data = NULL;
70		pwd.Length = 0;
71		return;
72	}
73	CFDataRef cfData = CFStringCreateExternalRepresentation(NULL,
74		str, kCFStringEncodingUTF8, 0);
75	if(cfData == NULL) {
76		printf("***p12ImportPassPhrase: can't convert passphrase to UTF8\n");
77		throw std::invalid_argument("bad passphrase");
78	}
79	unsigned keyLen = CFDataGetLength(cfData);
80	mCoder.allocItem(pwd, keyLen);
81	memmove(pwd.Data, CFDataGetBytePtr(cfData), keyLen);
82	CFRelease(cfData);
83}
84
85static void doIndent(unsigned depth)
86{
87	for(unsigned i=0; i<depth; i++) {
88		putchar(' ');
89	}
90}
91
92/* thread-unsafe oid-to-string converter */
93static char oidStrBuf[OID_PARSER_STRING_SIZE];
94
95static const char *oidStr(
96	const CSSM_OID &oid,
97	OidParser &parser)
98{
99	parser.oidParse(oid.Data, oid.Length, oidStrBuf);
100	return oidStrBuf;
101}
102
103static void printDataAsHex(
104	const CSSM_DATA *d,
105	unsigned maxToPrint = 0)		// optional, 0 means print it all
106{
107	unsigned i;
108	bool more = false;
109	uint32 len = d->Length;
110	uint8 *cp = d->Data;
111
112	if((maxToPrint != 0) && (len > maxToPrint)) {
113		len = maxToPrint;
114		more = true;
115	}
116	printf("len %lu : ", d->Length);
117	for(i=0; i<len; i++) {
118		printf("%02X ", ((unsigned char *)cp)[i]);
119	}
120	if(more) {
121		printf("...\n");
122	}
123	else {
124		printf("\n");
125	}
126}
127
128static void printDataAsUnichars(
129	const CSSM_DATA &data)
130{
131	if(data.Length & 1) {
132		printf("Unicode can not have odd number of bytes\n");
133		return;
134	}
135	/* don't assume anything endian... */
136	unsigned strLen = data.Length / 2;
137	UniChar *uc = (UniChar *)malloc(strLen * sizeof(UniChar));
138	const uint8 *inp = data.Data;
139	UniChar *outp = uc;
140	while(inp < (data.Data + data.Length)) {
141		*outp = (((unsigned)inp[0]) << 8) | inp[1];
142		outp++;
143		inp += 2;
144	}
145	char *outStr = NULL;
146	CFStringRef cstr = CFStringCreateWithCharacters(NULL, uc, strLen);
147	if(cstr == NULL) {
148		printf("***Error creating CFString from unichars\n");
149		goto errOut;
150	}
151
152	outStr = (char *)malloc(strLen + 1);
153	if(CFStringGetCString(cstr, outStr, strLen + 1, kCFStringEncodingASCII)) {
154		printf("%s\n", outStr);
155	}
156	else {
157		printf("***Error converting from unicode to ASCII\n");
158	}
159errOut:
160	free(uc);
161	if(outStr) {
162		free(outStr);
163	}
164	CFRelease(cstr);
165	return;
166}
167
168uint32 dataToInt(
169	const CSSM_DATA &cdata)
170{
171	if((cdata.Length == 0) || (cdata.Data == NULL)) {
172		return 0;
173	}
174	uint32 len = cdata.Length;
175	if(len > sizeof(uint32)) {
176		printf("***Bad formatting for DER integer\n");
177		len = sizeof(uint32);
178	}
179
180	uint32 rtn = 0;
181	uint8 *cp = cdata.Data;
182	for(uint32 i=0; i<len; i++) {
183		rtn = (rtn << 8) | *cp++;
184	}
185	return rtn;
186}
187
188#ifdef old_and_in_the_way
189static int writeAuthSafeContent(
190	const CSSM_DATA &rawBlob,
191	const char *outFile,
192	SecNssCoder &coder,
193	OidParser &parser)
194{
195	NSS_P12_RawPFX pfx;
196	memset(&pfx, 0, sizeof(pfx));
197	if(coder.decodeItem(rawBlob, NSS_P12_RawPFXTemplate, &pfx)) {
198		printf("***Error on top-level decode of NSS_P12_RawPFX\n");
199		return 1;
200	}
201	printf("...version = %u\n", (unsigned)dataToInt(pfx.version));
202	NSS_P7_RawContentInfo &rci = pfx.authSafe;
203	printf("...contentType = %s\n", oidStr(rci.contentType, parser));
204
205	/* parse content per OID the only special case is PKCS7_Data,
206	 * which we unwrap from an octet string before writing it */
207	CSSM_DATA toWrite;
208	if(nssCompareCssmData(&rci.contentType, &CSSMOID_PKCS7_Data)) {
209		if(coder.decodeItem(rci.content, SEC_OctetStringTemplate,
210				&toWrite)) {
211			printf("***Error decoding PKCS7_Data Octet string; writing"
212				" raw contents\n");
213			toWrite = rci.content;
214		}
215	}
216	else if(nssCompareCssmData(&rci.contentType,
217			&CSSMOID_PKCS7_SignedData)) {
218		/* the only other legal content type here */
219		/* This is encoded SignedData which I am not even close
220		 * to worrying about - Panther p12 won't do this */
221		toWrite = rci.content;
222	}
223	else {
224		printf("***writeAuthSafeContent: bad contentType\n");
225		return 1;
226	}
227	if(writeFile(outFile, toWrite.Data, toWrite.Length)) {
228		printf("***Error writing to %s\n", outFile);
229		return 1;
230	}
231	else {
232		printf("...%u bytes written to %s\n",
233			(unsigned)toWrite.Length, outFile);
234		return 0;
235	}
236}
237#endif	/* old_and_in_the_way */
238
239/*
240 * Decrypt the contents of a NSS_P7_EncryptedData
241 */
242#define WRITE_DECRYPT_TEXT	0
243#if 	WRITE_DECRYPT_TEXT
244static int ctr = 0;
245#endif
246
247#define IMPORT_EXPORT_COMPLETE  1
248
249static int encryptedDataDecrypt(
250	const NSS_P7_EncryptedData &edata,
251	P12ParseInfo &pinfo,
252	NSS_P12_PBE_Params *pbep,	// preparsed
253	CSSM_DATA &ptext)			// result goes here in pinfo.coder space
254{
255	/* see if we can grok the encr alg */
256	CSSM_ALGORITHMS		keyAlg;			// e.g., CSSM_ALGID_DES
257	CSSM_ALGORITHMS		encrAlg;		// e.g., CSSM_ALGID_3DES_3KEY_EDE
258	CSSM_ALGORITHMS		pbeHashAlg;		// SHA1 or MD5
259	uint32				keySizeInBits;
260	uint32				blockSizeInBytes;	// for IV, optional
261	CSSM_PADDING		padding;		// CSSM_PADDING_PKCS7, etc.
262	CSSM_ENCRYPT_MODE	mode;			// CSSM_ALGMODE_CBCPadIV8, etc.
263	#if IMPORT_EXPORT_COMPLETE
264	PKCS_Which			pkcs;
265
266	bool found = pkcsOidToParams(&edata.contentInfo.encrAlg.algorithm,
267		keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes,
268		padding, mode, pkcs);
269	#else
270	bool found = pkcsOidToParams(&edata.contentInfo.encrAlg.algorithm,
271		keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes,
272		padding, mode);
273	#endif  /* IMPORT_EXPORT_COMPLETE */
274
275	if(!found) {
276		printf("***EncryptedData encrAlg not understood\n");
277		return 1;
278	}
279
280	unsigned iterCount = dataToInt(pbep->iterations);
281
282	/* go */
283	CSSM_RETURN crtn = p12Decrypt_app(pinfo.mCspHand,
284		edata.contentInfo.encrContent,
285		keyAlg, encrAlg, pbeHashAlg,
286		keySizeInBits, blockSizeInBytes,
287		padding, mode,
288		iterCount, pbep->salt,
289		pinfo.mPwd,
290		pinfo.mCoder,
291		ptext);
292	#if WRITE_DECRYPT_TEXT
293	if(crtn == 0) {
294		char fname[100];
295		sprintf(fname, "decrypt%d.der", ctr++);
296		writeFile(fname, ptext.Data, ptext.Length);
297		printf("...wrote %u bytes to %s\n",
298			(unsigned)ptext.Length, fname);
299	}
300	#endif
301	return crtn ? 1 : 0;
302
303}
304
305
306/*
307 * Parse an CSSM_X509_ALGORITHM_IDENTIFIER specific to P12.
308 * Decode the alg params as a NSS_P12_PBE_Params and parse and
309 * return the result if the pbeParams is non-NULL.
310 */
311static int p12AlgIdParse(
312	const CSSM_X509_ALGORITHM_IDENTIFIER &algId,
313	NSS_P12_PBE_Params *pbeParams,		// optional
314	P12ParseInfo &pinfo,
315	unsigned depth)						// print indent depth
316{
317	doIndent(depth);
318	printf("encrAlg = %s\n", oidStr(algId.algorithm, pinfo.mParser));
319	const CSSM_DATA &param = algId.parameters;
320	if(pbeParams == NULL) {
321		/* alg params are uninterpreted */
322		doIndent(depth);
323		printf("Alg Params : ");
324		printDataAsHex(&param);
325		return 0;
326	}
327
328	if(param.Length == 0) {
329		printf("===warning: no alg parameters, this is not optional\n");
330		return 0;
331	}
332
333	memset(pbeParams, 0, sizeof(*pbeParams));
334	if(pinfo.mCoder.decodeItem(param,
335			NSS_P12_PBE_ParamsTemplate, pbeParams)) {
336		printf("***Error decoding NSS_P12_PBE_Params\n");
337		return 1;
338	}
339	doIndent(depth);
340	printf("Salt : ");
341	printDataAsHex(&pbeParams->salt);
342	doIndent(depth);
343	if(pbeParams->iterations.Length > 4) {
344		printf("warning: iterations greater than max int\n");
345		doIndent(depth);
346		printf("Iterations : ");
347		printDataAsHex(&pbeParams->iterations);
348	}
349	else {
350		printf("Iterations : %u\n",
351			(unsigned)dataToInt(pbeParams->iterations));
352	}
353	return 0;
354}
355
356/*
357 * Parse a NSS_P7_EncryptedData - specifically in the context
358 * of a P12 in password privacy mode. (The latter assumption is
359 * to enable us to infer CSSM_X509_ALGORITHM_IDENTIFIER.parameters
360 * format).
361 */
362static int encryptedDataParse(
363	const NSS_P7_EncryptedData &edata,
364	P12ParseInfo &pinfo,
365	NSS_P12_PBE_Params *pbep,		// optional, RETURNED
366	unsigned depth)					// print indent depth
367{
368	doIndent(depth);
369	printf("version = %u\n", (unsigned)dataToInt(edata.version));
370	const NSS_P7_EncrContentInfo &ci = edata.contentInfo;
371	doIndent(depth);
372	printf("contentType = %s\n", oidStr(ci.contentType, pinfo.mParser));
373
374	/*
375	 * Parse the alg ID, safe PBE params for when we do the
376	 * key unwrap
377	 */
378	const CSSM_X509_ALGORITHM_IDENTIFIER &algId = ci.encrAlg;
379	if(p12AlgIdParse(algId, pbep, pinfo, depth)) {
380		return 1;
381	}
382
383	doIndent(depth);
384	printf("encrContent : ");
385	printDataAsHex(&ci.encrContent, 12);
386	return 0;
387}
388
389static int attrParse(
390	const NSS_Attribute *attr,
391	P12ParseInfo &pinfo,
392	unsigned depth)
393{
394	doIndent(depth);
395	printf("attrType : %s\n", oidStr(attr->attrType, pinfo.mParser));
396	unsigned numVals = nssArraySize((const void **)attr->attrValue);
397	doIndent(depth);
398	printf("numValues = %u\n", numVals);
399
400	for(unsigned dex=0; dex<numVals; dex++) {
401		doIndent(depth);
402		printf("val[%u] : ", dex);
403
404		/*
405		 * Note: these two enumerated types should only have one att value
406		 * per PKCS9. Leave that to real apps, we want to see what's there
407		 * in any case.
408		 */
409		if(nssCompareCssmData(&attr->attrType, &CSSMOID_PKCS9_FriendlyName)) {
410			/* BMP string (UniCode) */
411			CSSM_DATA ustr;
412			if(pinfo.mCoder.decodeItem(*attr->attrValue[dex],
413					kSecAsn1BMPStringTemplate, &ustr)) {
414				printf("***Error decoding BMP string\n");
415				continue;
416			}
417			printDataAsUnichars(ustr);
418		}
419		else if(nssCompareCssmData(&attr->attrType,
420					&CSSMOID_PKCS9_LocalKeyId)) {
421			/* Octet string */
422			CSSM_DATA ostr;
423			if(pinfo.mCoder.decodeItem(*attr->attrValue[dex],
424					kSecAsn1OctetStringTemplate, &ostr)) {
425				printf("***Error decoding LocalKeyId string\n");
426				continue;
427			}
428			printDataAsHex(&ostr, 16);
429		}
430		else {
431			printDataAsHex(attr->attrValue[dex], 8);
432		}
433	}
434	return 0;
435}
436
437/*
438 * ShroudedKeyBag parser w/decrypt
439 */
440static int shroudedKeyBagParse(
441	const NSS_P12_ShroudedKeyBag *keyBag,
442	P12ParseInfo &pinfo,
443	unsigned depth)
444{
445	const CSSM_X509_ALGORITHM_IDENTIFIER &algId = keyBag->algorithm;
446	NSS_P12_PBE_Params pbep;
447	if(p12AlgIdParse(algId, &pbep, pinfo, depth)) {
448		return 1;
449	}
450	if(pinfo.mPwd.Data == NULL) {
451		doIndent(depth);
452		printf("=== Key not decrypted (no passphrase)===\n");
453		return 0;
454	}
455
456	/*
457	 * Prepare for decryption
458	 */
459	CSSM_ALGORITHMS		keyAlg;			// e.g., CSSM_ALGID_DES
460	CSSM_ALGORITHMS		encrAlg;		// e.g., CSSM_ALGID_3DES_3KEY_EDE
461	CSSM_ALGORITHMS		pbeHashAlg;		// SHA1 or MD5
462	uint32				keySizeInBits;
463	uint32				blockSizeInBytes;	// for IV, optional
464	CSSM_PADDING		padding;		// CSSM_PADDING_PKCS7, etc.
465	CSSM_ENCRYPT_MODE	mode;			// CSSM_ALGMODE_CBCPadIV8, etc.
466	#if IMPORT_EXPORT_COMPLETE
467	PKCS_Which			pkcs;
468
469	bool found = pkcsOidToParams(&algId.algorithm,
470		keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes,
471		padding, mode, pkcs);
472	#else
473	bool found = pkcsOidToParams(&algId.algorithm,
474		keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes,
475		padding, mode);
476	#endif
477
478	if(!found) {
479		printf("***ShroudedKeyBag encrAlg not understood\n");
480		return 1;
481	}
482
483	unsigned iterCount = dataToInt(pbep.iterations);
484	CSSM_DATA berPrivKey;
485
486	/* decrypt, result is BER encoded private key */
487	CSSM_RETURN crtn = p12Decrypt_app(pinfo.mCspHand,
488		keyBag->encryptedData,
489		keyAlg, encrAlg, pbeHashAlg,
490		keySizeInBits, blockSizeInBytes,
491		padding, mode,
492		iterCount, pbep.salt,
493		pinfo.mPwd,
494		pinfo.mCoder,
495		berPrivKey);
496	if(crtn) {
497		doIndent(depth);
498		printf("***Error decrypting private key\n");
499		return 1;
500	}
501
502	/* decode */
503	NSS_PrivateKeyInfo privKey;
504	memset(&privKey, 0, sizeof(privKey));
505	if(pinfo.mCoder.decodeItem(berPrivKey,
506			kSecAsn1PrivateKeyInfoTemplate, &privKey)) {
507		doIndent(depth);
508		printf("***Error decoding decrypted private key\n");
509		return 1;
510	}
511
512	/*
513	 * in P12 library, we'd convert the result into a CSSM_KEY
514	 * or a SecItem...
515	 */
516	CSSM_X509_ALGORITHM_IDENTIFIER &privAlg = privKey.algorithm;
517	doIndent(depth);
518	printf("Priv Key Alg  : %s\n", oidStr(privAlg.algorithm, pinfo.mParser));
519	doIndent(depth);
520	printf("Priv Key Blob : ");
521	printDataAsHex(&privKey.privateKey, 16);
522
523	unsigned numAttrs = nssArraySize((const void**)privKey.attributes);
524	if(numAttrs) {
525		doIndent(depth+3);
526		printf("numAttrs = %u\n", numAttrs);
527		for(unsigned i=0; i<numAttrs; i++) {
528			doIndent(depth+3);
529			printf("attr[%u]:\n", i);
530			attrParse(privKey.attributes[i], pinfo, depth+6);
531		}
532	}
533	return 0;
534}
535
536/*
537 * CertBag parser
538 */
539static int certBagParse(
540	const NSS_P12_CertBag *certBag,
541	P12ParseInfo &pinfo,
542	unsigned depth)
543{
544	/* fixe - we really need to store the attrs along with the cert here! */
545	switch(certBag->type) {
546		case CT_X509:
547			doIndent(depth);
548			printf("X509 cert found, size %u\n",
549				(unsigned)certBag->certValue.Length);
550			pinfo.mParsed.mCerts.addBlob(certBag->certValue);
551			break;
552		default:
553			doIndent(depth);
554			printf("Unknown cert type found\n");
555			P12UnknownBlob *uk = new P12UnknownBlob(certBag->certValue,
556				certBag->bagType);
557			pinfo.mParsed.mUnknown.addBlob(uk);
558	}
559	return 0;
560}
561
562/*
563 * CrlBag parser
564 */
565static int crlBagParse(
566	const NSS_P12_CrlBag *crlBag,
567	P12ParseInfo &pinfo,
568	unsigned depth)
569{
570	/* fixe - we really need to store the attrs along with the crl here! */
571	switch(crlBag->type) {
572		case CRT_X509:
573			doIndent(depth);
574			printf("X509 CRL found, size %u\n",
575				(unsigned)crlBag->crlValue.Length);
576			pinfo.mParsed.mCrls.addBlob(crlBag->crlValue);
577			break;
578		default:
579			doIndent(depth);
580			printf("Unknown CRL type found\n");
581			P12UnknownBlob *uk = new P12UnknownBlob(crlBag->crlValue,
582				crlBag->bagType);
583			pinfo.mParsed.mUnknown.addBlob(uk);
584	}
585	return 0;
586}
587
588
589/*
590 * Parse an encoded NSS_P12_SafeContents. This could be either
591 * present as plaintext in an AuthSafe or decrypted.
592 */
593static int safeContentsParse(
594	const CSSM_DATA &contentsBlob,
595	P12ParseInfo &pinfo,
596	unsigned depth)		// print indent depth
597{
598	NSS_P12_SafeContents sc;
599	memset(&sc, 0, sizeof(sc));
600	if(pinfo.mCoder.decodeItem(contentsBlob, NSS_P12_SafeContentsTemplate,
601			&sc)) {
602		printf("***Error decoding SafeContents\n");
603		return 1;
604	}
605	unsigned numBags = nssArraySize((const void **)sc.bags);
606	doIndent(depth);
607	printf("SafeContents num bags %u\n", numBags);
608	int rtn = 0;
609
610	for(unsigned dex=0; dex<numBags; dex++) {
611		NSS_P12_SafeBag *bag = sc.bags[dex];
612		doIndent(depth);
613		printf("Bag %u:\n", dex);
614
615		/* common stuff here */
616		doIndent(depth+3);
617		printf("bagId = %s\n", oidStr(bag->bagId, pinfo.mParser));
618		doIndent(depth+3);
619		printf("type = %s\n", p12BagTypeStr(bag->type));
620		unsigned numAttrs = nssArraySize((const void**)bag->bagAttrs);
621		if(numAttrs) {
622			doIndent(depth+3);
623			printf("numAttrs = %u\n", numAttrs);
624			for(unsigned i=0; i<numAttrs; i++) {
625				doIndent(depth+3);
626				printf("attr[%u]:\n", i);
627				attrParse(bag->bagAttrs[i], pinfo, depth+6);
628			}
629		}
630
631		/*
632		 * Now break out to individual bag type
633		 *
634		 * This hacked line breaks when we have a real key bag defined
635		 */
636		unsigned defaultLen = (unsigned)bag->bagValue.keyBag->Length;
637		switch(bag->type) {
638			case BT_KeyBag:
639				doIndent(depth+3);
640				printf("KeyBag: size %u\n", defaultLen);
641				break;
642			case BT_ShroudedKeyBag:
643				doIndent(depth+3);
644				printf("ShroudedKeyBag:\n");
645				rtn = shroudedKeyBagParse(bag->bagValue.shroudedKeyBag,
646					pinfo,
647					depth+6);
648				break;
649			case BT_CertBag:
650				doIndent(depth+3);
651				printf("CertBag:\n");
652				rtn = certBagParse(bag->bagValue.certBag,
653					pinfo,
654					depth+6);
655				break;
656			case BT_CrlBag:
657				doIndent(depth+3);
658				printf("CrlBag:\n");
659				rtn = crlBagParse(bag->bagValue.crlBag,
660					pinfo,
661					depth+6);
662				break;
663			case BT_SecretBag:
664				doIndent(depth+3);
665				printf("SecretBag: size %u\n", defaultLen);
666				break;
667			case BT_SafeContentsBag:
668				doIndent(depth+3);
669				printf("SafeContentsBag: size %u\n", defaultLen);
670				break;
671			default:
672				doIndent(depth+3);
673				printf("===Warning: unknownBagType (%u)\n",
674					(unsigned)bag->type);
675				break;
676		}
677		if(rtn) {
678			break;
679		}
680	}
681	return rtn;
682}
683
684/*
685 * Parse a ContentInfo in the context of (i.e., as an element of)
686 * an element in a AuthenticatedSafe
687 */
688static int authSafeElementParse(
689	const NSS_P7_DecodedContentInfo *info,
690	P12ParseInfo &pinfo,
691	unsigned depth)		// print indent depth
692{
693	char oidStr[OID_PARSER_STRING_SIZE];
694	pinfo.mParser.oidParse(info->contentType.Data,
695		info->contentType.Length, oidStr);
696
697	doIndent(depth);
698	printf("contentType = %s\n", oidStr);
699	doIndent(depth);
700	printf("type = %s\n", p7ContentInfoTypeStr(info->type));
701	int rtn = 0;
702	switch(info->type) {
703		case CT_Data:
704			/* unencrypted SafeContents */
705			doIndent(depth);
706			printf("raw size: %u\n",
707				(unsigned)info->content.data->Length);
708			doIndent(depth);
709			printf("Plaintext SafeContents:\n");
710			rtn = safeContentsParse(*info->content.data,
711				pinfo, depth+3);
712			break;
713
714		case CT_EncryptedData:
715		{
716			doIndent(depth);
717			printf("EncryptedData:\n");
718			NSS_P12_PBE_Params pbep;
719			rtn = encryptedDataParse(*info->content.encryptData,
720				pinfo, &pbep, depth+3);
721			if(rtn) {
722				break;
723			}
724			if(pinfo.mPwd.Data == NULL) {
725				doIndent(depth+3);
726				printf("=== Contents not decrypted (no passphrase)===\n");
727			}
728			else {
729				/*
730				* Decrypt contents to get a SafeContents and
731				* then parse that.
732				*/
733				CSSM_DATA ptext = {0, NULL};
734				rtn = encryptedDataDecrypt(*info->content.encryptData,
735					pinfo, &pbep, ptext);
736				doIndent(depth);
737				if(rtn) {
738					printf("***Error decrypting CT_EncryptedData\n");
739					break;
740				}
741				printf("Decrypted SafeContents {\n");
742				rtn = safeContentsParse(ptext, pinfo, depth+3);
743				doIndent(depth);
744				printf("}\n");
745			}
746			break;
747		}
748		default:
749			/* the rest map to an ASN_ANY/CSSM_DATA for now */
750			doIndent(depth+3);
751			printf("size of %u is all we know today\n",
752				(unsigned)info->content.data->Length);
753			rtn = 0;
754			break;
755	}
756	return rtn;
757}
758
759/*
760 * Parse an encoded NSS_P12_AuthenticatedSafe
761 */
762static int authSafeParse(
763	const CSSM_DATA authSafeBlob,
764	P12ParseInfo &pinfo,
765	unsigned depth)		// print indent depth
766{
767	NSS_P12_AuthenticatedSafe authSafe;
768
769	memset(&authSafe, 0, sizeof(authSafe));
770	if(pinfo.mCoder.decodeItem(authSafeBlob,
771			NSS_P12_AuthenticatedSafeTemplate,
772			&authSafe)) {
773		printf("***Error decoding authSafe\n");
774		return 1;
775	}
776	unsigned numInfos = nssArraySize((const void **)authSafe.info);
777	doIndent(depth);
778	printf("authSafe numInfos %u\n", numInfos);
779
780	int rtn = 0;
781	for(unsigned dex=0; dex<numInfos; dex++) {
782		NSS_P7_DecodedContentInfo *info = authSafe.info[dex];
783		doIndent(depth);
784		printf("AuthSafe.info[%u] {\n", dex);
785		rtn = authSafeElementParse(info, pinfo, depth+3);
786		if(rtn) {
787			break;
788		}
789		doIndent(depth);
790		printf("}\n");
791	}
792	return rtn;
793}
794
795static int p12MacParse(
796	const NSS_P12_MacData &macData,
797	P12ParseInfo &pinfo,
798	unsigned depth)		// print indent depth
799{
800	if(p12AlgIdParse(macData.mac.digestAlgorithm, NULL, pinfo, depth)) {
801		return 1;
802	}
803	doIndent(depth);
804	printf("Digest : ");
805	printDataAsHex(&macData.mac.digest, 20);
806	doIndent(depth);
807	printf("Salt : ");
808	printDataAsHex(&macData.macSalt, 16);
809	const CSSM_DATA &iter = macData.iterations;
810
811	if(iter.Length > 4) {
812		doIndent(depth);
813		printf("***Warning: malformed iteraton length (%u)\n",
814			(unsigned)iter.Length);
815	}
816	unsigned i = dataToInt(iter);
817	doIndent(depth);
818	printf("Iterations = %u\n", i);
819	return 0;
820}
821
822static int p12Parse(
823	const CSSM_DATA &rawBlob,
824	P12ParseInfo &pinfo,
825	unsigned depth)		// print indent depth
826{
827	NSS_P12_DecodedPFX pfx;
828	memset(&pfx, 0, sizeof(pfx));
829	if(pinfo.mCoder.decodeItem(rawBlob, NSS_P12_DecodedPFXTemplate, &pfx)) {
830		printf("***Error on top-level decode of NSS_P12_DecodedPFX\n");
831		return 1;
832	}
833	doIndent(depth);
834	printf("version = %u\n", (unsigned)dataToInt(pfx.version));
835	NSS_P7_DecodedContentInfo &dci = pfx.authSafe;
836
837	doIndent(depth);
838	printf("contentType = %s\n", oidStr(dci.contentType, pinfo.mParser));
839	doIndent(depth);
840	printf("type = %s\n", p7ContentInfoTypeStr(dci.type));
841	int rtn = 0;
842	if(nssCompareCssmData(&dci.contentType, &CSSMOID_PKCS7_Data)) {
843		doIndent(depth);
844		printf("AuthenticatedSafe Length %u {\n",
845			(unsigned)dci.content.data->Length);
846		rtn = authSafeParse(*dci.content.data, pinfo, depth+3);
847		doIndent(depth);
848		printf("}\n");
849	}
850	else {
851		printf("Not parsing any other content type today.\n");
852	}
853	if(pfx.macData) {
854		doIndent(depth);
855		printf("Mac Data {\n");
856		p12MacParse(*pfx.macData, pinfo, depth+3);
857		doIndent(depth);
858		printf("}\n");
859		if(pinfo.mPwd.Data == NULL) {
860			doIndent(depth);
861			printf("=== MAC not verified (no passphrase)===\n");
862		}
863		else {
864			CSSM_RETURN crtn = p12VerifyMac_app(pfx, pinfo.mCspHand,
865				pinfo.mPwd, pinfo.mCoder);
866			doIndent(depth);
867			if(crtn) {
868				cssmPerror("p12VerifyMac", crtn);
869				doIndent(depth);
870				printf("***MAC verify failure.\n");
871			}
872			else {
873				printf("MAC verifies OK.\n");
874			}
875		}
876	}
877	return 0;
878}
879
880int p12ParseTop(
881	CSSM_DATA		&rawBlob,
882	CSSM_CSP_HANDLE cspHand,
883	CFStringRef 	pwd,
884	bool 			verbose)
885{
886	SecNssCoder coder;
887	OidParser parser;
888	P12Parsed parsed(coder);
889	P12ParseInfo pinfo(coder,
890		cspHand,
891		parser,
892		pwd,
893		NULL,			// no separate pwd
894		parsed);
895
896	printf("PKCS12 PFX:\n");
897	int rtn = p12Parse(rawBlob, pinfo, 3);
898
899	/* find anything? */
900	if(verbose) {
901		P12KnownBlobs &certs = pinfo.mParsed.mCerts;
902		if(certs.mNumBlobs) {
903			printf("\n\n");
904			for(unsigned dex=0; dex<certs.mNumBlobs; dex++) {
905				printf("Cert %u:\n", dex);
906				printCert(certs.mBlobs[dex].Data,
907					certs.mBlobs[dex].Length, CSSM_FALSE);
908				printf("\n");
909			}
910		}
911		P12KnownBlobs &crls = pinfo.mParsed.mCrls;
912		if(crls.mNumBlobs) {
913			printf("\n\n");
914			for(unsigned dex=0; dex<crls.mNumBlobs; dex++) {
915				printf("CRL %u:\n", dex);
916				printCrl(crls.mBlobs[dex].Data,
917					crls.mBlobs[dex].Length, CSSM_FALSE);
918			}
919		}
920	}
921	return rtn;
922}
923