X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/clxutils/p12/p12Parse.cpp diff --git a/SecurityTests/clxutils/p12/p12Parse.cpp b/SecurityTests/clxutils/p12/p12Parse.cpp new file mode 100644 index 00000000..d5f4b5f0 --- /dev/null +++ b/SecurityTests/clxutils/p12/p12Parse.cpp @@ -0,0 +1,919 @@ +/* + * 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 "SecNssCoder.h" +#include +#include +#include +#include +#include +#include +#include +#include "p12Crypto.h" +#include +#include "pkcs12Parsed.h" +#include +#include +#include +#include +#include +#include + +/* + * 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) + { + pwdToUcode(macPwd, mPwd); + pwdToUcode(encrPwd, mEncrPwd); + } + + ~P12ParseInfo() {} + + void pwdToUcode(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::pwdToUcode( + CFStringRef str, + CSSM_DATA &pwd) +{ + if(str == NULL) { + pwd.Data = NULL; + pwd.Length = 0; + return; + } + CFIndex len = CFStringGetLength(str); + mCoder.allocItem(pwd, (len * sizeof(UniChar)) + 2); + uint8 *cp = pwd.Data; + for(CFIndex dex=0; dex> 8; + *cp++ = uc & 0xff; + } + *cp++ = 0; + *cp++ = 0; +} + +static void doIndent(unsigned depth) +{ + for(unsigned i=0; iLength; + uint8 *cp = d->Data; + + if((maxToPrint != 0) && (len > maxToPrint)) { + len = maxToPrint; + more = true; + } + for(i=0; i sizeof(uint32)) { + printf("***Bad formatting for DER integer\n"); + len = sizeof(uint32); + } + + uint32 rtn = 0; + uint8 *cp = cdata.Data; + for(uint32 i=0; iiterations); + + /* 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; dexattrType, &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], + kSecAsn1ObjectIDTemplate, &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; itype) { + 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; dexbagId, 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; ibagAttrs[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 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