X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_pkcs12/lib/pkcs12Decode.cpp diff --git a/Security/libsecurity_pkcs12/lib/pkcs12Decode.cpp b/Security/libsecurity_pkcs12/lib/pkcs12Decode.cpp new file mode 100644 index 00000000..9e426c1a --- /dev/null +++ b/Security/libsecurity_pkcs12/lib/pkcs12Decode.cpp @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2003-2004,2011,2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * pkcs12Decode.h - P12Coder decoding engine. + */ + +#include "pkcs12Coder.h" +#include "pkcs12Templates.h" +#include "pkcs12Utils.h" +#include "pkcs12Debug.h" +#include "pkcs12Crypto.h" +#include +#include + +/* top-level PKCS12 PFX decoder */ +void P12Coder::decode( + CFDataRef cdpfx) +{ + SecNssCoder localCdr; + NSS_P12_DecodedPFX pfx; + + p12DecodeLog("decode"); + memset(&pfx, 0, sizeof(pfx)); + const CSSM_DATA rawBlob = {CFDataGetLength(cdpfx), + (uint8 *)CFDataGetBytePtr(cdpfx)}; + + if(localCdr.decodeItem(rawBlob, NSS_P12_DecodedPFXTemplate, &pfx)) { + p12ErrorLog("Error on top-level decode of NSS_P12_DecodedPFX\n"); + P12_THROW_DECODE; + } + NSS_P7_DecodedContentInfo &dci = pfx.authSafe; + if(dci.type != CT_Data) { + /* no other types supported yet */ + p12ErrorLog("bad top-level contentType\n"); + P12_THROW_DECODE; + } + mIntegrityMode = kSecPkcs12ModePassword; + + if(pfx.macData == NULL) { + /* not present is an error in kSecPkcs12ModePassword */ + p12ErrorLog("no MAC in PFX\n"); + P12_THROW_DECODE; + } + macParse(*pfx.macData, localCdr); + + const CSSM_DATA *macPhrase = getMacPassPhrase(); + const CSSM_KEY *macPassKey = getMacPassKey(); + if((macPhrase == NULL) && (macPassKey == NULL)) { + p12ErrorLog("no passphrase set\n"); + CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE); + } + CSSM_RETURN crtn = p12VerifyMac(pfx, mCspHand, macPhrase, + macPassKey, localCdr); + if(crtn) { + p12LogCssmError("p12VerifyMac", crtn); + CssmError::throwMe(errSecPkcs12VerifyFailure); + } + + authSafeParse(*dci.content.data, localCdr); + + /* + * On success, if we have a keychain, store certs and CRLs there + */ + if(mKeychain != NULL) { + storeDecodeResults(); + } +} + +/* + * Decrypt the contents of a NSS_P7_EncryptedData + */ +void P12Coder::encryptedDataDecrypt( + const NSS_P7_EncryptedData &edata, + SecNssCoder &localCdr, + NSS_P12_PBE_Params *pbep, // preparsed + CSSM_DATA &ptext) // result goes here in localCdr space +{ + p12DecodeLog("encryptedDataDecrypt"); + + /* 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. + PKCS_Which pkcs; + + bool found = pkcsOidToParams(&edata.contentInfo.encrAlg.algorithm, + keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes, + padding, mode, pkcs); + if(!found || (pkcs != PW_PKCS12)) { + p12ErrorLog("EncryptedData encrAlg not understood\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); + } + + uint32 iterCount; + if(!p12DataToInt(pbep->iterations, iterCount)) { + p12ErrorLog("encryptedDataDecrypt: badly formed iterCount\n"); + P12_THROW_DECODE; + } + const CSSM_DATA *pwd = getEncrPassPhrase(); + const CSSM_KEY *passKey = getEncrPassKey(); + if((pwd == NULL) && (passKey == NULL)) { + p12ErrorLog("no passphrase set\n"); + CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE); + } + + /* go */ + CSSM_RETURN crtn = p12Decrypt(mCspHand, + edata.contentInfo.encrContent, + keyAlg, encrAlg, pbeHashAlg, + keySizeInBits, blockSizeInBytes, + padding, mode, + iterCount, pbep->salt, + pwd, + passKey, + localCdr, + ptext); + if(crtn) { + CssmError::throwMe(crtn); + } +} + + +/* + * 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. + */ +void P12Coder::algIdParse( + const CSSM_X509_ALGORITHM_IDENTIFIER &algId, + NSS_P12_PBE_Params *pbeParams, // optional + SecNssCoder &localCdr) +{ + p12DecodeLog("algIdParse"); + + const CSSM_DATA ¶m = algId.parameters; + if(pbeParams == NULL) { + /* alg params are uninterpreted */ + return; + } + + if(param.Length == 0) { + p12ErrorLog("algIdParse: no alg parameters\n"); + P12_THROW_DECODE; + } + + memset(pbeParams, 0, sizeof(*pbeParams)); + if(localCdr.decodeItem(param, + NSS_P12_PBE_ParamsTemplate, pbeParams)) { + p12ErrorLog("Error decoding NSS_P12_PBE_Params\n"); + P12_THROW_DECODE; + } +} + +/* + * 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). + */ +void P12Coder::encryptedDataParse( + const NSS_P7_EncryptedData &edata, + SecNssCoder &localCdr, + NSS_P12_PBE_Params *pbep) // optional, RETURNED +{ + p12DecodeLog("encryptedDataParse"); + + /* + * Parse the alg ID, save PBE params for when we do the decrypt + * key unwrap + */ + const NSS_P7_EncrContentInfo &ci = edata.contentInfo; + const CSSM_X509_ALGORITHM_IDENTIFIER &algId = ci.encrAlg; + algIdParse(algId, pbep, localCdr); +} + +/* + * ShroudedKeyBag parser w/decrypt + */ +void P12Coder::shroudedKeyBagParse( + const NSS_P12_SafeBag &safeBag, + SecNssCoder &localCdr) +{ + p12DecodeLog("Found shrouded key bag"); + if(mPrivKeyImportState == PKIS_NoMore) { + CssmError::throwMe(errSecMultiplePrivKeys); + } + + const NSS_P12_ShroudedKeyBag *keyBag = safeBag.bagValue.shroudedKeyBag; + const CSSM_X509_ALGORITHM_IDENTIFIER &algId = keyBag->algorithm; + NSS_P12_PBE_Params pbep; + algIdParse(algId, &pbep, localCdr); + + /* + * 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. + PKCS_Which pkcs; + + bool found = pkcsOidToParams(&algId.algorithm, + keyAlg, encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes, + padding, mode, pkcs); + if(!found || (pkcs != PW_PKCS12)) { + p12ErrorLog("ShroudedKeyBag encrAlg not understood\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); + } + + uint32 iterCount; + if(!p12DataToInt(pbep.iterations, iterCount)) { + p12ErrorLog("ShroudedKeyBag: badly formed iterCount\n"); + P12_THROW_DECODE; + } + const CSSM_DATA *encrPhrase = getEncrPassPhrase(); + const CSSM_KEY *passKey = getEncrPassKey(); + if((encrPhrase == NULL) && (passKey == NULL)) { + p12ErrorLog("no passphrase set\n"); + CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_PASSPHRASE); + } + + /* We'll own the actual CSSM_KEY memory */ + CSSM_KEY_PTR privKey = (CSSM_KEY_PTR)mCoder.malloc(sizeof(CSSM_KEY)); + memset(privKey, 0, sizeof(CSSM_KEY)); + + CSSM_DATA labelData; + p12GenLabel(labelData, localCdr); + + CSSM_RETURN crtn = p12UnwrapKey(mCspHand, + mDlDbHand.DLHandle ? &mDlDbHand : NULL, + mImportFlags & kSecImportKeys, + keyBag->encryptedData, + keyAlg, encrAlg, pbeHashAlg, + keySizeInBits, blockSizeInBytes, + padding, mode, + iterCount, pbep.salt, + encrPhrase, + passKey, + localCdr, + labelData, + mAccess, + mNoAcl, + mKeyUsage, + mKeyAttrs, + privKey); + if(crtn) { + p12ErrorLog("Error unwrapping private key\n"); + CssmError::throwMe(crtn); + } + p12DecodeLog("unwrapped shrouded key bag"); + + P12KeyBag *p12bag = new P12KeyBag(privKey, mCspHand, + safeBag.bagAttrs, labelData, mCoder); + addKey(p12bag); + + if(mPrivKeyImportState == PKIS_AllowOne) { + mPrivKeyImportState = PKIS_NoMore; + } +} + +/* + * (unshrouded) KeyBag parser + */ +void P12Coder::keyBagParse( + const NSS_P12_SafeBag &safeBag, + SecNssCoder &localCdr) +{ + if(mPrivKeyImportState == PKIS_NoMore) { + CssmError::throwMe(errSecMultiplePrivKeys); + } + + /* FIXME - should be able to parse and handle this.... */ + p12DecodeLog("found keyBag"); + NSS_P12_KeyBag *keyBag = safeBag.bagValue.keyBag; + P12OpaqueBag *p12Bag = new P12OpaqueBag(safeBag.bagId, + /* this breaks when NSS_P12_KeyBag is not a CSSM_DATA */ + *keyBag, + safeBag.bagAttrs, + mCoder); + addOpaque(p12Bag); +} + +/* + * CertBag parser + */ +void P12Coder::certBagParse( + const NSS_P12_SafeBag &safeBag, + SecNssCoder &localCdr) +{ + p12DecodeLog("found certBag"); + NSS_P12_CertBag *certBag = safeBag.bagValue.certBag; + switch(certBag->type) { + case CT_X509: + case CT_SDSI: + break; + default: + p12ErrorLog("certBagParse: unknown cert type\n"); + P12_THROW_DECODE; + } + P12CertBag *p12Bag = new P12CertBag(certBag->type, + certBag->certValue, + safeBag.bagAttrs, + mCoder); + addCert(p12Bag); +} + +/* + * CrlBag parser + */ +void P12Coder::crlBagParse( + const NSS_P12_SafeBag &safeBag, + SecNssCoder &localCdr) +{ + p12DecodeLog("found crlBag"); + NSS_P12_CrlBag *crlBag = safeBag.bagValue.crlBag; + switch(crlBag->type) { + case CRT_X509: + break; + default: + p12ErrorLog("crlBagParse: unknown CRL type\n"); + P12_THROW_DECODE; + } + P12CrlBag *p12Bag = new P12CrlBag(crlBag->type, + crlBag->crlValue, + safeBag.bagAttrs, + mCoder); + addCrl(p12Bag); +} + +/* + * SecretBag parser + */ +void P12Coder::secretBagParse( + const NSS_P12_SafeBag &safeBag, + SecNssCoder &localCdr) +{ + p12DecodeLog("found secretBag"); + NSS_P12_SecretBag *secretBag = safeBag.bagValue.secretBag; + P12OpaqueBag *p12Bag = new P12OpaqueBag(safeBag.bagId, + /* this breaks when NSS_P12_SecretBag is not a CSSM_DATA */ + *secretBag, + safeBag.bagAttrs, + mCoder); + addOpaque(p12Bag); +} + +/* + * SafeContentsBag parser + */ +void P12Coder::safeContentsBagParse( + const NSS_P12_SafeBag &safeBag, + SecNssCoder &localCdr) +{ + p12DecodeLog("found SafeContents safe bag"); + NSS_P12_SafeContentsBag *scBag = safeBag.bagValue.safeContentsBag; + P12OpaqueBag *p12Bag = new P12OpaqueBag(safeBag.bagId, + /* this breaks when NSS_P12_SafeContentsBag is not a CSSM_DATA */ + *scBag, + safeBag.bagAttrs, + mCoder); + addOpaque(p12Bag); +} + +/* + * Parse an encoded NSS_P12_SafeContents. This could be either + * present as plaintext in an AuthSafe or decrypted. + */ +void P12Coder::safeContentsParse( + const CSSM_DATA &contentsBlob, + SecNssCoder &localCdr) +{ + p12DecodeLog("safeContentsParse"); + + NSS_P12_SafeContents sc; + memset(&sc, 0, sizeof(sc)); + if(localCdr.decodeItem(contentsBlob, NSS_P12_SafeContentsTemplate, + &sc)) { + p12ErrorLog("Error decoding SafeContents\n"); + P12_THROW_DECODE; + } + unsigned numBags = nssArraySize((const void **)sc.bags); + for(unsigned dex=0; dexbagValue.keyBag == NULL) { + p12ErrorLog("safeContentsParse: Empty SafeBag\n"); + P12_THROW_DECODE; + } + + /* + * Break out to individual bag type + */ + switch(bag->type) { + case BT_KeyBag: + keyBagParse(*bag, localCdr); + break; + case BT_ShroudedKeyBag: + shroudedKeyBagParse(*bag, localCdr); + break; + case BT_CertBag: + certBagParse(*bag, localCdr); + break; + case BT_CrlBag: + crlBagParse(*bag, localCdr); + break; + case BT_SecretBag: + secretBagParse(*bag ,localCdr); + break; + case BT_SafeContentsBag: + safeContentsBagParse(*bag, localCdr); + break; + default: + p12ErrorLog("unknown p12 BagType (%u)\n", + (unsigned)bag->type); + P12_THROW_DECODE; + } + } +} + +/* + * Parse a ContentInfo in the context of (i.e., as an element of) + * an AuthenticatedSafe. + */ +void P12Coder::authSafeElementParse( + const NSS_P7_DecodedContentInfo *info, + SecNssCoder &localCdr) +{ + p12DecodeLog("authSafeElementParse"); + switch(info->type) { + case CT_Data: + /* unencrypted SafeContents */ + safeContentsParse(*info->content.data, localCdr); + break; + + case CT_EncryptedData: + { + NSS_P12_PBE_Params pbep; + encryptedDataParse(*info->content.encryptData, localCdr, &pbep); + + /* + * Decrypt contents to get a SafeContents and + * then parse that. + */ + CSSM_DATA ptext = {0, NULL}; + encryptedDataDecrypt(*info->content.encryptData, + localCdr, &pbep, ptext); + safeContentsParse(ptext, localCdr); + break; + } + default: + p12ErrorLog("authSafeElementParse: unknown sage type (%u)\n", + (unsigned)info->type); + + /* well, save it as an opaque bag for now */ + P12OpaqueBag *opaque = new P12OpaqueBag( + info->contentType, *info->content.data, + NULL, // no attrs + localCdr); + addOpaque(opaque); + break; + } +} + +/* + * Parse an encoded NSS_P12_AuthenticatedSafe + */ +void P12Coder::authSafeParse( + const CSSM_DATA &authSafeBlob, + SecNssCoder &localCdr) +{ + p12DecodeLog("authSafeParse"); + + NSS_P12_AuthenticatedSafe authSafe; + + memset(&authSafe, 0, sizeof(authSafe)); + if(localCdr.decodeItem(authSafeBlob, + NSS_P12_AuthenticatedSafeTemplate, + &authSafe)) { + p12ErrorLog("Error decoding authSafe\n"); + P12_THROW_DECODE; + } + unsigned numInfos = nssArraySize((const void **)authSafe.info); + for(unsigned dex=0; dex 4) { + p12ErrorLog("malformed iteration length (%u)\n", + (unsigned)iter.Length); + P12_THROW_DECODE; + } +} +