--- /dev/null
+/*
+ * grunt-quality p12 parse tool.
+ *
+ * The PFX ripper in this file uses, and always will use, the
+ * app-space reference PBE and crypto routines in p12Crypto.{h,cpp}
+ * and p12pbe.{h,cpp} in this directory.
+ */
+#include "p12Parse.h"
+#include "p12Crypto.h"
+#include "pkcs12Parsed.h"
+#include "pkcs12Utils.h"
+#include <Security/asn1Templates.h>
+#include <security_asn1/nssUtils.h>
+#include <security_asn1/SecNssCoder.h>
+#include <security_cdsa_utils/cuOidParser.h>
+#include <security_cdsa_utils/cuPrintCert.h>
+#include <security_pkcs12/pkcs7Templates.h>
+#include <security_pkcs12/pkcs12Templates.h>
+#include <security_cdsa_utils/cuFileIo.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <security_cdsa_utils/cuCdsaUtils.h>
+#include <Security/oidsattr.h>
+#include <stdexcept>
+
+/*
+ * The stuff which gets passed around to all parse modules
+ */
+class P12ParseInfo
+{
+public:
+ P12ParseInfo(SecNssCoder &coder,
+ CSSM_CSP_HANDLE cspHand,
+ OidParser &parser,
+ /* NULL means don't verify MAC, don't decrypt */
+ CFStringRef macPwd,
+ /* if this second pwd is absent, use macPwd for both */
+ CFStringRef encrPwd,
+ P12Parsed &parsed) // destination
+ : mCoder(coder),
+ mCspHand(cspHand),
+ mParser(parser),
+ mParsed(parsed)
+ {
+ importPwd(macPwd, mPwd);
+ importPwd(encrPwd, mEncrPwd);
+ }
+
+ ~P12ParseInfo() {}
+
+ void importPwd(CFStringRef str, CSSM_DATA &pwd);
+
+ SecNssCoder &mCoder;
+ CSSM_CSP_HANDLE mCspHand;
+ OidParser &mParser;
+ CSSM_DATA mPwd; // unicode, double null terminated
+ CSSM_DATA mEncrPwd;
+ P12Parsed &mParsed; // destination
+
+};
+
+void P12ParseInfo::importPwd(
+ CFStringRef str,
+ CSSM_DATA &pwd)
+{
+ if(str == NULL) {
+ pwd.Data = NULL;
+ pwd.Length = 0;
+ return;
+ }
+ CFDataRef cfData = CFStringCreateExternalRepresentation(NULL,
+ str, kCFStringEncodingUTF8, 0);
+ if(cfData == NULL) {
+ printf("***p12ImportPassPhrase: can't convert passphrase to UTF8\n");
+ throw std::invalid_argument("bad passphrase");
+ }
+ unsigned keyLen = CFDataGetLength(cfData);
+ mCoder.allocItem(pwd, keyLen);
+ memmove(pwd.Data, CFDataGetBytePtr(cfData), keyLen);
+ CFRelease(cfData);
+}
+
+static void doIndent(unsigned depth)
+{
+ for(unsigned i=0; i<depth; i++) {
+ putchar(' ');
+ }
+}
+
+/* thread-unsafe oid-to-string converter */
+static char oidStrBuf[OID_PARSER_STRING_SIZE];
+
+static const char *oidStr(
+ const CSSM_OID &oid,
+ OidParser &parser)
+{
+ parser.oidParse(oid.Data, oid.Length, oidStrBuf);
+ return oidStrBuf;
+}
+
+static void printDataAsHex(
+ const CSSM_DATA *d,
+ unsigned maxToPrint = 0) // optional, 0 means print it all
+{
+ unsigned i;
+ bool more = false;
+ uint32 len = d->Length;
+ uint8 *cp = d->Data;
+
+ if((maxToPrint != 0) && (len > maxToPrint)) {
+ len = maxToPrint;
+ more = true;
+ }
+ printf("len %lu : ", d->Length);
+ for(i=0; i<len; i++) {
+ printf("%02X ", ((unsigned char *)cp)[i]);
+ }
+ if(more) {
+ printf("...\n");
+ }
+ else {
+ printf("\n");
+ }
+}
+
+static void printDataAsUnichars(
+ const CSSM_DATA &data)
+{
+ if(data.Length & 1) {
+ printf("Unicode can not have odd number of bytes\n");
+ return;
+ }
+ /* don't assume anything endian... */
+ unsigned strLen = data.Length / 2;
+ UniChar *uc = (UniChar *)malloc(strLen * sizeof(UniChar));
+ const uint8 *inp = data.Data;
+ UniChar *outp = uc;
+ while(inp < (data.Data + data.Length)) {
+ *outp = (((unsigned)inp[0]) << 8) | inp[1];
+ outp++;
+ inp += 2;
+ }
+ char *outStr = NULL;
+ CFStringRef cstr = CFStringCreateWithCharacters(NULL, uc, strLen);
+ if(cstr == NULL) {
+ printf("***Error creating CFString from unichars\n");
+ goto errOut;
+ }
+
+ outStr = (char *)malloc(strLen + 1);
+ if(CFStringGetCString(cstr, outStr, strLen + 1, kCFStringEncodingASCII)) {
+ printf("%s\n", outStr);
+ }
+ else {
+ printf("***Error converting from unicode to ASCII\n");
+ }
+errOut:
+ free(uc);
+ if(outStr) {
+ free(outStr);
+ }
+ CFRelease(cstr);
+ return;
+}
+
+uint32 dataToInt(
+ const CSSM_DATA &cdata)
+{
+ if((cdata.Length == 0) || (cdata.Data == NULL)) {
+ return 0;
+ }
+ uint32 len = cdata.Length;
+ if(len > sizeof(uint32)) {
+ printf("***Bad formatting for DER integer\n");
+ len = sizeof(uint32);
+ }
+
+ uint32 rtn = 0;
+ uint8 *cp = cdata.Data;
+ for(uint32 i=0; i<len; i++) {
+ rtn = (rtn << 8) | *cp++;
+ }
+ return rtn;
+}
+
+#ifdef old_and_in_the_way
+static int writeAuthSafeContent(
+ const CSSM_DATA &rawBlob,
+ const char *outFile,
+ SecNssCoder &coder,
+ OidParser &parser)
+{
+ NSS_P12_RawPFX pfx;
+ memset(&pfx, 0, sizeof(pfx));
+ if(coder.decodeItem(rawBlob, NSS_P12_RawPFXTemplate, &pfx)) {
+ printf("***Error on top-level decode of NSS_P12_RawPFX\n");
+ return 1;
+ }
+ printf("...version = %u\n", (unsigned)dataToInt(pfx.version));
+ NSS_P7_RawContentInfo &rci = pfx.authSafe;
+ printf("...contentType = %s\n", oidStr(rci.contentType, parser));
+
+ /* parse content per OID the only special case is PKCS7_Data,
+ * which we unwrap from an octet string before writing it */
+ CSSM_DATA toWrite;
+ if(nssCompareCssmData(&rci.contentType, &CSSMOID_PKCS7_Data)) {
+ if(coder.decodeItem(rci.content, SEC_OctetStringTemplate,
+ &toWrite)) {
+ printf("***Error decoding PKCS7_Data Octet string; writing"
+ " raw contents\n");
+ toWrite = rci.content;
+ }
+ }
+ else if(nssCompareCssmData(&rci.contentType,
+ &CSSMOID_PKCS7_SignedData)) {
+ /* the only other legal content type here */
+ /* This is encoded SignedData which I am not even close
+ * to worrying about - Panther p12 won't do this */
+ toWrite = rci.content;
+ }
+ else {
+ printf("***writeAuthSafeContent: bad contentType\n");
+ return 1;
+ }
+ if(writeFile(outFile, toWrite.Data, toWrite.Length)) {
+ printf("***Error writing to %s\n", outFile);
+ return 1;
+ }
+ else {
+ printf("...%u bytes written to %s\n",
+ (unsigned)toWrite.Length, outFile);
+ return 0;
+ }
+}
+#endif /* old_and_in_the_way */
+
+/*
+ * Decrypt the contents of a NSS_P7_EncryptedData
+ */
+#define WRITE_DECRYPT_TEXT 0
+#if WRITE_DECRYPT_TEXT
+static int ctr = 0;
+#endif
+
+#define IMPORT_EXPORT_COMPLETE 1
+
+static int encryptedDataDecrypt(
+ const NSS_P7_EncryptedData &edata,
+ P12ParseInfo &pinfo,
+ NSS_P12_PBE_Params *pbep, // preparsed
+ CSSM_DATA &ptext) // result goes here in pinfo.coder space
+{
+ /* see if we can grok the encr alg */
+ CSSM_ALGORITHMS keyAlg; // e.g., CSSM_ALGID_DES
+ CSSM_ALGORITHMS encrAlg; // e.g., CSSM_ALGID_3DES_3KEY_EDE
+ CSSM_ALGORITHMS pbeHashAlg; // SHA1 or MD5
+ uint32 keySizeInBits;
+ uint32 blockSizeInBytes; // for IV, optional
+ CSSM_PADDING padding; // CSSM_PADDING_PKCS7, etc.
+ CSSM_ENCRYPT_MODE mode; // CSSM_ALGMODE_CBCPadIV8, etc.
+ #if IMPORT_EXPORT_COMPLETE
+ PKCS_Which pkcs;
+
+ bool found = pkcsOidToParams(&edata.contentInfo.encrAlg.algorithm,
+ keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes,
+ padding, mode, pkcs);
+ #else
+ bool found = pkcsOidToParams(&edata.contentInfo.encrAlg.algorithm,
+ keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes,
+ padding, mode);
+ #endif /* IMPORT_EXPORT_COMPLETE */
+
+ if(!found) {
+ printf("***EncryptedData encrAlg not understood\n");
+ return 1;
+ }
+
+ unsigned iterCount = dataToInt(pbep->iterations);
+
+ /* go */
+ CSSM_RETURN crtn = p12Decrypt_app(pinfo.mCspHand,
+ edata.contentInfo.encrContent,
+ keyAlg, encrAlg, pbeHashAlg,
+ keySizeInBits, blockSizeInBytes,
+ padding, mode,
+ iterCount, pbep->salt,
+ pinfo.mPwd,
+ pinfo.mCoder,
+ ptext);
+ #if WRITE_DECRYPT_TEXT
+ if(crtn == 0) {
+ char fname[100];
+ sprintf(fname, "decrypt%d.der", ctr++);
+ writeFile(fname, ptext.Data, ptext.Length);
+ printf("...wrote %u bytes to %s\n",
+ (unsigned)ptext.Length, fname);
+ }
+ #endif
+ return crtn ? 1 : 0;
+
+}
+
+
+/*
+ * Parse an CSSM_X509_ALGORITHM_IDENTIFIER specific to P12.
+ * Decode the alg params as a NSS_P12_PBE_Params and parse and
+ * return the result if the pbeParams is non-NULL.
+ */
+static int p12AlgIdParse(
+ const CSSM_X509_ALGORITHM_IDENTIFIER &algId,
+ NSS_P12_PBE_Params *pbeParams, // optional
+ P12ParseInfo &pinfo,
+ unsigned depth) // print indent depth
+{
+ doIndent(depth);
+ printf("encrAlg = %s\n", oidStr(algId.algorithm, pinfo.mParser));
+ const CSSM_DATA ¶m = algId.parameters;
+ if(pbeParams == NULL) {
+ /* alg params are uninterpreted */
+ doIndent(depth);
+ printf("Alg Params : ");
+ printDataAsHex(¶m);
+ return 0;
+ }
+
+ if(param.Length == 0) {
+ printf("===warning: no alg parameters, this is not optional\n");
+ return 0;
+ }
+
+ memset(pbeParams, 0, sizeof(*pbeParams));
+ if(pinfo.mCoder.decodeItem(param,
+ NSS_P12_PBE_ParamsTemplate, pbeParams)) {
+ printf("***Error decoding NSS_P12_PBE_Params\n");
+ return 1;
+ }
+ doIndent(depth);
+ printf("Salt : ");
+ printDataAsHex(&pbeParams->salt);
+ doIndent(depth);
+ if(pbeParams->iterations.Length > 4) {
+ printf("warning: iterations greater than max int\n");
+ doIndent(depth);
+ printf("Iterations : ");
+ printDataAsHex(&pbeParams->iterations);
+ }
+ else {
+ printf("Iterations : %u\n",
+ (unsigned)dataToInt(pbeParams->iterations));
+ }
+ return 0;
+}
+
+/*
+ * Parse a NSS_P7_EncryptedData - specifically in the context
+ * of a P12 in password privacy mode. (The latter assumption is
+ * to enable us to infer CSSM_X509_ALGORITHM_IDENTIFIER.parameters
+ * format).
+ */
+static int encryptedDataParse(
+ const NSS_P7_EncryptedData &edata,
+ P12ParseInfo &pinfo,
+ NSS_P12_PBE_Params *pbep, // optional, RETURNED
+ unsigned depth) // print indent depth
+{
+ doIndent(depth);
+ printf("version = %u\n", (unsigned)dataToInt(edata.version));
+ const NSS_P7_EncrContentInfo &ci = edata.contentInfo;
+ doIndent(depth);
+ printf("contentType = %s\n", oidStr(ci.contentType, pinfo.mParser));
+
+ /*
+ * Parse the alg ID, safe PBE params for when we do the
+ * key unwrap
+ */
+ const CSSM_X509_ALGORITHM_IDENTIFIER &algId = ci.encrAlg;
+ if(p12AlgIdParse(algId, pbep, pinfo, depth)) {
+ return 1;
+ }
+
+ doIndent(depth);
+ printf("encrContent : ");
+ printDataAsHex(&ci.encrContent, 12);
+ return 0;
+}
+
+static int attrParse(
+ const NSS_Attribute *attr,
+ P12ParseInfo &pinfo,
+ unsigned depth)
+{
+ doIndent(depth);
+ printf("attrType : %s\n", oidStr(attr->attrType, pinfo.mParser));
+ unsigned numVals = nssArraySize((const void **)attr->attrValue);
+ doIndent(depth);
+ printf("numValues = %u\n", numVals);
+
+ for(unsigned dex=0; dex<numVals; dex++) {
+ doIndent(depth);
+ printf("val[%u] : ", dex);
+
+ /*
+ * Note: these two enumerated types should only have one att value
+ * per PKCS9. Leave that to real apps, we want to see what's there
+ * in any case.
+ */
+ if(nssCompareCssmData(&attr->attrType, &CSSMOID_PKCS9_FriendlyName)) {
+ /* BMP string (UniCode) */
+ CSSM_DATA ustr;
+ if(pinfo.mCoder.decodeItem(*attr->attrValue[dex],
+ kSecAsn1BMPStringTemplate, &ustr)) {
+ printf("***Error decoding BMP string\n");
+ continue;
+ }
+ printDataAsUnichars(ustr);
+ }
+ else if(nssCompareCssmData(&attr->attrType,
+ &CSSMOID_PKCS9_LocalKeyId)) {
+ /* Octet string */
+ CSSM_DATA ostr;
+ if(pinfo.mCoder.decodeItem(*attr->attrValue[dex],
+ kSecAsn1OctetStringTemplate, &ostr)) {
+ printf("***Error decoding LocalKeyId string\n");
+ continue;
+ }
+ printDataAsHex(&ostr, 16);
+ }
+ else {
+ printDataAsHex(attr->attrValue[dex], 8);
+ }
+ }
+ return 0;
+}
+
+/*
+ * ShroudedKeyBag parser w/decrypt
+ */
+static int shroudedKeyBagParse(
+ const NSS_P12_ShroudedKeyBag *keyBag,
+ P12ParseInfo &pinfo,
+ unsigned depth)
+{
+ const CSSM_X509_ALGORITHM_IDENTIFIER &algId = keyBag->algorithm;
+ NSS_P12_PBE_Params pbep;
+ if(p12AlgIdParse(algId, &pbep, pinfo, depth)) {
+ return 1;
+ }
+ if(pinfo.mPwd.Data == NULL) {
+ doIndent(depth);
+ printf("=== Key not decrypted (no passphrase)===\n");
+ return 0;
+ }
+
+ /*
+ * Prepare for decryption
+ */
+ CSSM_ALGORITHMS keyAlg; // e.g., CSSM_ALGID_DES
+ CSSM_ALGORITHMS encrAlg; // e.g., CSSM_ALGID_3DES_3KEY_EDE
+ CSSM_ALGORITHMS pbeHashAlg; // SHA1 or MD5
+ uint32 keySizeInBits;
+ uint32 blockSizeInBytes; // for IV, optional
+ CSSM_PADDING padding; // CSSM_PADDING_PKCS7, etc.
+ CSSM_ENCRYPT_MODE mode; // CSSM_ALGMODE_CBCPadIV8, etc.
+ #if IMPORT_EXPORT_COMPLETE
+ PKCS_Which pkcs;
+
+ bool found = pkcsOidToParams(&algId.algorithm,
+ keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes,
+ padding, mode, pkcs);
+ #else
+ bool found = pkcsOidToParams(&algId.algorithm,
+ keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes,
+ padding, mode);
+ #endif
+
+ if(!found) {
+ printf("***ShroudedKeyBag encrAlg not understood\n");
+ return 1;
+ }
+
+ unsigned iterCount = dataToInt(pbep.iterations);
+ CSSM_DATA berPrivKey;
+
+ /* decrypt, result is BER encoded private key */
+ CSSM_RETURN crtn = p12Decrypt_app(pinfo.mCspHand,
+ keyBag->encryptedData,
+ keyAlg, encrAlg, pbeHashAlg,
+ keySizeInBits, blockSizeInBytes,
+ padding, mode,
+ iterCount, pbep.salt,
+ pinfo.mPwd,
+ pinfo.mCoder,
+ berPrivKey);
+ if(crtn) {
+ doIndent(depth);
+ printf("***Error decrypting private key\n");
+ return 1;
+ }
+
+ /* decode */
+ NSS_PrivateKeyInfo privKey;
+ memset(&privKey, 0, sizeof(privKey));
+ if(pinfo.mCoder.decodeItem(berPrivKey,
+ kSecAsn1PrivateKeyInfoTemplate, &privKey)) {
+ doIndent(depth);
+ printf("***Error decoding decrypted private key\n");
+ return 1;
+ }
+
+ /*
+ * in P12 library, we'd convert the result into a CSSM_KEY
+ * or a SecItem...
+ */
+ CSSM_X509_ALGORITHM_IDENTIFIER &privAlg = privKey.algorithm;
+ doIndent(depth);
+ printf("Priv Key Alg : %s\n", oidStr(privAlg.algorithm, pinfo.mParser));
+ doIndent(depth);
+ printf("Priv Key Blob : ");
+ printDataAsHex(&privKey.privateKey, 16);
+
+ unsigned numAttrs = nssArraySize((const void**)privKey.attributes);
+ if(numAttrs) {
+ doIndent(depth+3);
+ printf("numAttrs = %u\n", numAttrs);
+ for(unsigned i=0; i<numAttrs; i++) {
+ doIndent(depth+3);
+ printf("attr[%u]:\n", i);
+ attrParse(privKey.attributes[i], pinfo, depth+6);
+ }
+ }
+ return 0;
+}
+
+/*
+ * CertBag parser
+ */
+static int certBagParse(
+ const NSS_P12_CertBag *certBag,
+ P12ParseInfo &pinfo,
+ unsigned depth)
+{
+ /* fixe - we really need to store the attrs along with the cert here! */
+ switch(certBag->type) {
+ case CT_X509:
+ doIndent(depth);
+ printf("X509 cert found, size %u\n",
+ (unsigned)certBag->certValue.Length);
+ pinfo.mParsed.mCerts.addBlob(certBag->certValue);
+ break;
+ default:
+ doIndent(depth);
+ printf("Unknown cert type found\n");
+ P12UnknownBlob *uk = new P12UnknownBlob(certBag->certValue,
+ certBag->bagType);
+ pinfo.mParsed.mUnknown.addBlob(uk);
+ }
+ return 0;
+}
+
+/*
+ * CrlBag parser
+ */
+static int crlBagParse(
+ const NSS_P12_CrlBag *crlBag,
+ P12ParseInfo &pinfo,
+ unsigned depth)
+{
+ /* fixe - we really need to store the attrs along with the crl here! */
+ switch(crlBag->type) {
+ case CRT_X509:
+ doIndent(depth);
+ printf("X509 CRL found, size %u\n",
+ (unsigned)crlBag->crlValue.Length);
+ pinfo.mParsed.mCrls.addBlob(crlBag->crlValue);
+ break;
+ default:
+ doIndent(depth);
+ printf("Unknown CRL type found\n");
+ P12UnknownBlob *uk = new P12UnknownBlob(crlBag->crlValue,
+ crlBag->bagType);
+ pinfo.mParsed.mUnknown.addBlob(uk);
+ }
+ return 0;
+}
+
+
+/*
+ * Parse an encoded NSS_P12_SafeContents. This could be either
+ * present as plaintext in an AuthSafe or decrypted.
+ */
+static int safeContentsParse(
+ const CSSM_DATA &contentsBlob,
+ P12ParseInfo &pinfo,
+ unsigned depth) // print indent depth
+{
+ NSS_P12_SafeContents sc;
+ memset(&sc, 0, sizeof(sc));
+ if(pinfo.mCoder.decodeItem(contentsBlob, NSS_P12_SafeContentsTemplate,
+ &sc)) {
+ printf("***Error decoding SafeContents\n");
+ return 1;
+ }
+ unsigned numBags = nssArraySize((const void **)sc.bags);
+ doIndent(depth);
+ printf("SafeContents num bags %u\n", numBags);
+ int rtn = 0;
+
+ for(unsigned dex=0; dex<numBags; dex++) {
+ NSS_P12_SafeBag *bag = sc.bags[dex];
+ doIndent(depth);
+ printf("Bag %u:\n", dex);
+
+ /* common stuff here */
+ doIndent(depth+3);
+ printf("bagId = %s\n", oidStr(bag->bagId, pinfo.mParser));
+ doIndent(depth+3);
+ printf("type = %s\n", p12BagTypeStr(bag->type));
+ unsigned numAttrs = nssArraySize((const void**)bag->bagAttrs);
+ if(numAttrs) {
+ doIndent(depth+3);
+ printf("numAttrs = %u\n", numAttrs);
+ for(unsigned i=0; i<numAttrs; i++) {
+ doIndent(depth+3);
+ printf("attr[%u]:\n", i);
+ attrParse(bag->bagAttrs[i], pinfo, depth+6);
+ }
+ }
+
+ /*
+ * Now break out to individual bag type
+ *
+ * This hacked line breaks when we have a real key bag defined
+ */
+ unsigned defaultLen = (unsigned)bag->bagValue.keyBag->Length;
+ switch(bag->type) {
+ case BT_KeyBag:
+ doIndent(depth+3);
+ printf("KeyBag: size %u\n", defaultLen);
+ break;
+ case BT_ShroudedKeyBag:
+ doIndent(depth+3);
+ printf("ShroudedKeyBag:\n");
+ rtn = shroudedKeyBagParse(bag->bagValue.shroudedKeyBag,
+ pinfo,
+ depth+6);
+ break;
+ case BT_CertBag:
+ doIndent(depth+3);
+ printf("CertBag:\n");
+ rtn = certBagParse(bag->bagValue.certBag,
+ pinfo,
+ depth+6);
+ break;
+ case BT_CrlBag:
+ doIndent(depth+3);
+ printf("CrlBag:\n");
+ rtn = crlBagParse(bag->bagValue.crlBag,
+ pinfo,
+ depth+6);
+ break;
+ case BT_SecretBag:
+ doIndent(depth+3);
+ printf("SecretBag: size %u\n", defaultLen);
+ break;
+ case BT_SafeContentsBag:
+ doIndent(depth+3);
+ printf("SafeContentsBag: size %u\n", defaultLen);
+ break;
+ default:
+ doIndent(depth+3);
+ printf("===Warning: unknownBagType (%u)\n",
+ (unsigned)bag->type);
+ break;
+ }
+ if(rtn) {
+ break;
+ }
+ }
+ return rtn;
+}
+
+/*
+ * Parse a ContentInfo in the context of (i.e., as an element of)
+ * an element in a AuthenticatedSafe
+ */
+static int authSafeElementParse(
+ const NSS_P7_DecodedContentInfo *info,
+ P12ParseInfo &pinfo,
+ unsigned depth) // print indent depth
+{
+ char oidStr[OID_PARSER_STRING_SIZE];
+ pinfo.mParser.oidParse(info->contentType.Data,
+ info->contentType.Length, oidStr);
+
+ doIndent(depth);
+ printf("contentType = %s\n", oidStr);
+ doIndent(depth);
+ printf("type = %s\n", p7ContentInfoTypeStr(info->type));
+ int rtn = 0;
+ switch(info->type) {
+ case CT_Data:
+ /* unencrypted SafeContents */
+ doIndent(depth);
+ printf("raw size: %u\n",
+ (unsigned)info->content.data->Length);
+ doIndent(depth);
+ printf("Plaintext SafeContents:\n");
+ rtn = safeContentsParse(*info->content.data,
+ pinfo, depth+3);
+ break;
+
+ case CT_EncryptedData:
+ {
+ doIndent(depth);
+ printf("EncryptedData:\n");
+ NSS_P12_PBE_Params pbep;
+ rtn = encryptedDataParse(*info->content.encryptData,
+ pinfo, &pbep, depth+3);
+ if(rtn) {
+ break;
+ }
+ if(pinfo.mPwd.Data == NULL) {
+ doIndent(depth+3);
+ printf("=== Contents not decrypted (no passphrase)===\n");
+ }
+ else {
+ /*
+ * Decrypt contents to get a SafeContents and
+ * then parse that.
+ */
+ CSSM_DATA ptext = {0, NULL};
+ rtn = encryptedDataDecrypt(*info->content.encryptData,
+ pinfo, &pbep, ptext);
+ doIndent(depth);
+ if(rtn) {
+ printf("***Error decrypting CT_EncryptedData\n");
+ break;
+ }
+ printf("Decrypted SafeContents {\n");
+ rtn = safeContentsParse(ptext, pinfo, depth+3);
+ doIndent(depth);
+ printf("}\n");
+ }
+ break;
+ }
+ default:
+ /* the rest map to an ASN_ANY/CSSM_DATA for now */
+ doIndent(depth+3);
+ printf("size of %u is all we know today\n",
+ (unsigned)info->content.data->Length);
+ rtn = 0;
+ break;
+ }
+ return rtn;
+}
+
+/*
+ * Parse an encoded NSS_P12_AuthenticatedSafe
+ */
+static int authSafeParse(
+ const CSSM_DATA authSafeBlob,
+ P12ParseInfo &pinfo,
+ unsigned depth) // print indent depth
+{
+ NSS_P12_AuthenticatedSafe authSafe;
+
+ memset(&authSafe, 0, sizeof(authSafe));
+ if(pinfo.mCoder.decodeItem(authSafeBlob,
+ NSS_P12_AuthenticatedSafeTemplate,
+ &authSafe)) {
+ printf("***Error decoding authSafe\n");
+ return 1;
+ }
+ unsigned numInfos = nssArraySize((const void **)authSafe.info);
+ doIndent(depth);
+ printf("authSafe numInfos %u\n", numInfos);
+
+ int rtn = 0;
+ for(unsigned dex=0; dex<numInfos; dex++) {
+ NSS_P7_DecodedContentInfo *info = authSafe.info[dex];
+ doIndent(depth);
+ printf("AuthSafe.info[%u] {\n", dex);
+ rtn = authSafeElementParse(info, pinfo, depth+3);
+ if(rtn) {
+ break;
+ }
+ doIndent(depth);
+ printf("}\n");
+ }
+ return rtn;
+}
+
+static int p12MacParse(
+ const NSS_P12_MacData &macData,
+ P12ParseInfo &pinfo,
+ unsigned depth) // print indent depth
+{
+ if(p12AlgIdParse(macData.mac.digestAlgorithm, NULL, pinfo, depth)) {
+ return 1;
+ }
+ doIndent(depth);
+ printf("Digest : ");
+ printDataAsHex(&macData.mac.digest, 20);
+ doIndent(depth);
+ printf("Salt : ");
+ printDataAsHex(&macData.macSalt, 16);
+ const CSSM_DATA &iter = macData.iterations;
+
+ if(iter.Length > 4) {
+ doIndent(depth);
+ printf("***Warning: malformed iteraton length (%u)\n",
+ (unsigned)iter.Length);
+ }
+ unsigned i = dataToInt(iter);
+ doIndent(depth);
+ printf("Iterations = %u\n", i);
+ return 0;
+}
+
+static int p12Parse(
+ const CSSM_DATA &rawBlob,
+ P12ParseInfo &pinfo,
+ unsigned depth) // print indent depth
+{
+ NSS_P12_DecodedPFX pfx;
+ memset(&pfx, 0, sizeof(pfx));
+ if(pinfo.mCoder.decodeItem(rawBlob, NSS_P12_DecodedPFXTemplate, &pfx)) {
+ printf("***Error on top-level decode of NSS_P12_DecodedPFX\n");
+ return 1;
+ }
+ doIndent(depth);
+ printf("version = %u\n", (unsigned)dataToInt(pfx.version));
+ NSS_P7_DecodedContentInfo &dci = pfx.authSafe;
+
+ doIndent(depth);
+ printf("contentType = %s\n", oidStr(dci.contentType, pinfo.mParser));
+ doIndent(depth);
+ printf("type = %s\n", p7ContentInfoTypeStr(dci.type));
+ int rtn = 0;
+ if(nssCompareCssmData(&dci.contentType, &CSSMOID_PKCS7_Data)) {
+ doIndent(depth);
+ printf("AuthenticatedSafe Length %u {\n",
+ (unsigned)dci.content.data->Length);
+ rtn = authSafeParse(*dci.content.data, pinfo, depth+3);
+ doIndent(depth);
+ printf("}\n");
+ }
+ else {
+ printf("Not parsing any other content type today.\n");
+ }
+ if(pfx.macData) {
+ doIndent(depth);
+ printf("Mac Data {\n");
+ p12MacParse(*pfx.macData, pinfo, depth+3);
+ doIndent(depth);
+ printf("}\n");
+ if(pinfo.mPwd.Data == NULL) {
+ doIndent(depth);
+ printf("=== MAC not verified (no passphrase)===\n");
+ }
+ else {
+ CSSM_RETURN crtn = p12VerifyMac_app(pfx, pinfo.mCspHand,
+ pinfo.mPwd, pinfo.mCoder);
+ doIndent(depth);
+ if(crtn) {
+ cssmPerror("p12VerifyMac", crtn);
+ doIndent(depth);
+ printf("***MAC verify failure.\n");
+ }
+ else {
+ printf("MAC verifies OK.\n");
+ }
+ }
+ }
+ return 0;
+}
+
+int p12ParseTop(
+ CSSM_DATA &rawBlob,
+ CSSM_CSP_HANDLE cspHand,
+ CFStringRef pwd,
+ bool verbose)
+{
+ SecNssCoder coder;
+ OidParser parser;
+ P12Parsed parsed(coder);
+ P12ParseInfo pinfo(coder,
+ cspHand,
+ parser,
+ pwd,
+ NULL, // no separate pwd
+ parsed);
+
+ printf("PKCS12 PFX:\n");
+ int rtn = p12Parse(rawBlob, pinfo, 3);
+
+ /* find anything? */
+ if(verbose) {
+ P12KnownBlobs &certs = pinfo.mParsed.mCerts;
+ if(certs.mNumBlobs) {
+ printf("\n\n");
+ for(unsigned dex=0; dex<certs.mNumBlobs; dex++) {
+ printf("Cert %u:\n", dex);
+ printCert(certs.mBlobs[dex].Data,
+ certs.mBlobs[dex].Length, CSSM_FALSE);
+ printf("\n");
+ }
+ }
+ P12KnownBlobs &crls = pinfo.mParsed.mCrls;
+ if(crls.mNumBlobs) {
+ printf("\n\n");
+ for(unsigned dex=0; dex<crls.mNumBlobs; dex++) {
+ printf("CRL %u:\n", dex);
+ printCrl(crls.mBlobs[dex].Data,
+ crls.mBlobs[dex].Length, CSSM_FALSE);
+ }
+ }
+ }
+ return rtn;
+}