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