//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1996 - 1999
//
//  File:       msgprov.cpp
//
//  Contents:   Microsoft Internet Security Authenticode Policy Provider
//
//  Functions:  SoftpubLoadMessage
//
//              *** local functions ***
//              _LoadSIP
//              _SetSubjectInfo
//              _GetMessage
//              _ExplodeMessage
//              _NoContentWrap
//              _SkipOverIdentifierAndLengthOctets
//
//  History:    05-Jun-1997 pberkman   created
//
//--------------------------------------------------------------------------

#include    "global.hxx"
#include    "crypthlp.h"

#include    "sipguids.h"    // located in pki/mssip32

BOOL _LoadSIP(CRYPT_PROVIDER_DATA *pProvData);
BOOL _SetSubjectInfo(CRYPT_PROVIDER_DATA *pProvData);
BOOL _GetMessage(CRYPT_PROVIDER_DATA *pProvData);
BOOL _ExplodeMessage(CRYPT_PROVIDER_DATA *pProvData);
BOOL _NoContentWrap(const BYTE *pbDER, DWORD cbDER);
DWORD _SkipOverIdentifierAndLengthOctets(const BYTE *pbDER, DWORD cbDER);
extern "C" BOOL MsCatConstructHashTag (IN DWORD cbDigest, IN LPBYTE pbDigest, OUT LPWSTR* ppwszHashTag);
extern "C" VOID MsCatFreeHashTag (IN LPWSTR pwszHashTag);


HRESULT WINAPI SoftpubLoadMessage(CRYPT_PROVIDER_DATA *pProvData)
{
    if (!(pProvData->padwTrustStepErrors) ||
        (pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_WVTINIT] != ERROR_SUCCESS) ||
        (pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] != ERROR_SUCCESS))
    {
        return(S_FALSE);
    }

    pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = ERROR_SUCCESS;

    switch (pProvData->pWintrustData->dwUnionChoice)
    {
        case WTD_CHOICE_CERT:
        case WTD_CHOICE_SIGNER:
                    //
                    // this is handled in the signature provider
                    //
                    return(ERROR_SUCCESS);

        case WTD_CHOICE_FILE:
        case WTD_CHOICE_CATALOG:
        case WTD_CHOICE_BLOB:
                    break;

        default:
                    pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = ERROR_INVALID_PARAMETER;
                    return(S_FALSE);
    }

    //
    //  extract the message from object.
    //
    if (!(_SetSubjectInfo(pProvData)))
    {
        return(S_FALSE);
    }

    if (!(_LoadSIP(pProvData)))
    {
        return(S_FALSE);
    }

    if (!(_GetMessage(pProvData)))
    {
        return(S_FALSE);
    }

    if (!(_ExplodeMessage(pProvData)))
    {
        return(S_FALSE);
    }


    //
    //  verify the object that the message pertains to
    //
    if ((pProvData->pWintrustData->dwUnionChoice == WTD_CHOICE_CATALOG) &&
        (_ISINSTRUCT(WINTRUST_CATALOG_INFO, pProvData->pWintrustData->pCatalog->cbStruct,
                     cbCalculatedFileHash)) &&
        (pProvData->pWintrustData->pCatalog->pbCalculatedFileHash) &&
        (pProvData->pWintrustData->pCatalog->cbCalculatedFileHash > 0))
    {
        //
        //  we've been passed in the calculated file hash so don't redo it, just check it!
        //
        if (!(pProvData->pPDSip->psIndirectData) ||
            !(pProvData->pPDSip->psIndirectData->Digest.pbData) ||
            (pProvData->pWintrustData->pCatalog->cbCalculatedFileHash !=
                pProvData->pPDSip->psIndirectData->Digest.cbData) ||
            (memcmp(pProvData->pWintrustData->pCatalog->pbCalculatedFileHash,
                    pProvData->pPDSip->psIndirectData->Digest.pbData,
                    pProvData->pPDSip->psIndirectData->Digest.cbData) != 0))
        {
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_BAD_DIGEST;
            return(S_FALSE);
        }

    }
    else
    {
        //
        //  we need to calculate the hash from the file.... do it!
        //
        if (!(pProvData->pPDSip->pSip->pfVerify(pProvData->pPDSip->psSipSubjectInfo,
                                                 pProvData->pPDSip->psIndirectData)))
        {
            if (GetLastError() == CRYPT_E_SECURITY_SETTINGS)
            {
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = CRYPT_E_SECURITY_SETTINGS;
            }
            else
            {
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_BAD_DIGEST;
            }

            return(S_FALSE);
        }
    }

    return(ERROR_SUCCESS);
}

static GUID     _gCATSubject = CRYPT_SUBJTYPE_CATALOG_IMAGE;

BOOL _LoadSIP(CRYPT_PROVIDER_DATA *pProvData)
{
    if (!(pProvData->pPDSip->pSip))
    {
        if (!(pProvData->pPDSip->pSip = (SIP_DISPATCH_INFO *)pProvData->psPfns->pfnAlloc(sizeof(SIP_DISPATCH_INFO))))
        {
            pProvData->dwError = GetLastError();
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_SYSTEM_ERROR;
            return(FALSE);
        }

        if (!(CryptSIPLoad(&pProvData->pPDSip->gSubject, 0, pProvData->pPDSip->pSip)))
        {
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_SIP]             = GetLastError();
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_PROVIDER_UNKNOWN;
            return(FALSE);
        }
    }

    if (pProvData->pWintrustData->dwUnionChoice == WTD_CHOICE_CATALOG)
    {
        if (!(pProvData->pPDSip->pCATSip))
        {
            if (!(pProvData->pPDSip->pCATSip = (SIP_DISPATCH_INFO *)pProvData->psPfns->pfnAlloc(sizeof(SIP_DISPATCH_INFO))))
            {
                pProvData->dwError = GetLastError();
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_SYSTEM_ERROR;
                return(FALSE);
            }

            if (!(CryptSIPLoad(&_gCATSubject, 0, pProvData->pPDSip->pCATSip)))
            {
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_SIP] = GetLastError();
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_PROVIDER_UNKNOWN;
                return(FALSE);
            }
        }
    }

    return(TRUE);
}

BOOL _SetSubjectInfo(CRYPT_PROVIDER_DATA *pProvData)
{
    SIP_SUBJECTINFO     *pSubjInfo;
    SIP_DISPATCH_INFO   sSIPDisp;

    switch (pProvData->pWintrustData->dwUnionChoice)
    {
        case WTD_CHOICE_BLOB:
            if (!(pProvData->pWintrustData->pBlob) ||
                !(_ISINSTRUCT(WINTRUST_BLOB_INFO, pProvData->pWintrustData->pBlob->cbStruct, pbMemSignedMsg)))
            {
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = (DWORD)ERROR_INVALID_PARAMETER;
                return(FALSE);
            }

            memcpy(&pProvData->pPDSip->gSubject, &pProvData->pWintrustData->pBlob->gSubject, sizeof(GUID));
            break;

        case WTD_CHOICE_FILE:
            if (!(pProvData->pWintrustData->pFile) ||
                !(_ISINSTRUCT(WINTRUST_FILE_INFO, pProvData->pWintrustData->pFile->cbStruct, hFile)))
            {
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = (DWORD)ERROR_INVALID_PARAMETER;
                return(FALSE);
            }

            if ((_ISINSTRUCT(WINTRUST_FILE_INFO, pProvData->pWintrustData->pFile->cbStruct, pgKnownSubject)) &&
                (pProvData->pWintrustData->pFile->pgKnownSubject))
            {
                memcpy(&pProvData->pPDSip->gSubject, pProvData->pWintrustData->pFile->pgKnownSubject, sizeof(GUID));
            }
            else if (!(CryptSIPRetrieveSubjectGuid(pProvData->pWintrustData->pFile->pcwszFilePath,
                                                   pProvData->pWintrustData->pFile->hFile,
                                                   &pProvData->pPDSip->gSubject)))
            {
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_SIP]             = GetLastError();
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV]   = TRUST_E_SUBJECT_FORM_UNKNOWN;
                return(FALSE);
            }
            break;

        case WTD_CHOICE_CATALOG:
            if (!(pProvData->pWintrustData->pCatalog) ||
                !(_ISINSTRUCT(WINTRUST_CATALOG_INFO, pProvData->pWintrustData->pCatalog->cbStruct,
                              hMemberFile)))
            {
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = (DWORD)ERROR_INVALID_PARAMETER;
                return(FALSE);
            }

            if ((_ISINSTRUCT(CRYPT_PROVIDER_DATA, pProvData->cbStruct, fRecallWithState)) &&
                (pProvData->fRecallWithState))
            {
                break;
            }

            if (!(pProvData->pPDSip->psSipCATSubjectInfo =
                    (SIP_SUBJECTINFO *)pProvData->psPfns->pfnAlloc(sizeof(SIP_SUBJECTINFO))))
            {
                pProvData->dwError = GetLastError();
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_SYSTEM_ERROR;
                return(FALSE);
            }

            memset(pProvData->pPDSip->psSipCATSubjectInfo, 0x00, sizeof(SIP_SUBJECTINFO));

            pProvData->pPDSip->psSipCATSubjectInfo->cbSize              = sizeof(SIP_SUBJECTINFO);
            pProvData->pPDSip->psSipCATSubjectInfo->hProv               = pProvData->hProv;
            pProvData->pPDSip->psSipCATSubjectInfo->pClientData         = pProvData->pWintrustData->pSIPClientData;
            pProvData->pPDSip->psSipCATSubjectInfo->pwsFileName         =
                            (WCHAR *)pProvData->pWintrustData->pCatalog->pcwszCatalogFilePath;
            pProvData->pPDSip->psSipCATSubjectInfo->pwsDisplayName      =
                                                        pProvData->pPDSip->psSipCATSubjectInfo->pwsFileName;

            pProvData->pPDSip->psSipCATSubjectInfo->fdwCAPISettings     = pProvData->dwRegPolicySettings;
            pProvData->pPDSip->psSipCATSubjectInfo->fdwSecuritySettings = pProvData->dwRegPolicySettings;

            if (!(pProvData->pPDSip->psSipCATSubjectInfo->pgSubjectType =
                            (GUID *)pProvData->psPfns->pfnAlloc(sizeof(GUID))))
            {
                pProvData->dwError = GetLastError();
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_SYSTEM_ERROR;
                return(FALSE);
            }

            memcpy(pProvData->pPDSip->psSipCATSubjectInfo->pgSubjectType, &_gCATSubject, sizeof(GUID));
            break;

        default:
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_CATALOGFILE]     = GetLastError();
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV]   = CRYPT_E_FILE_ERROR;
            return(FALSE);
    }


    //
    //  setup the subject info for the SIP
    //
    if (!(pProvData->pPDSip->psSipSubjectInfo))
    {
        if (!(pProvData->pPDSip->psSipSubjectInfo =
                (SIP_SUBJECTINFO *)pProvData->psPfns->pfnAlloc(sizeof(SIP_SUBJECTINFO))))
        {
            pProvData->dwError = GetLastError();
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_SYSTEM_ERROR;
            return(FALSE);
        }

        pSubjInfo = pProvData->pPDSip->psSipSubjectInfo;

        memset(pSubjInfo, 0x00, sizeof(SIP_SUBJECTINFO));

        pSubjInfo->cbSize   = sizeof(SIP_SUBJECTINFO);

        pSubjInfo->hProv    = pProvData->hProv;

    }
    else
    {
        pSubjInfo = pProvData->pPDSip->psSipSubjectInfo;
    }


    pSubjInfo->pClientData          = pProvData->pWintrustData->pSIPClientData;

    pSubjInfo->pwsFileName          = WTHelperGetFileName(pProvData->pWintrustData);
    pSubjInfo->hFile                = WTHelperGetFileHandle(pProvData->pWintrustData);
    pSubjInfo->pwsDisplayName       = pSubjInfo->pwsFileName;

    pSubjInfo->fdwCAPISettings      = pProvData->dwRegPolicySettings;
    pSubjInfo->fdwSecuritySettings  = pProvData->dwRegSecuritySettings;

    if (!(pSubjInfo->pgSubjectType = (GUID *)pProvData->psPfns->pfnAlloc(sizeof(GUID))))
    {
        pProvData->dwError = GetLastError();
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_SYSTEM_ERROR;
        return(FALSE);
    }

    switch(pProvData->pWintrustData->dwUnionChoice)
    {
        case WTD_CHOICE_FILE:
            break;

        case WTD_CHOICE_BLOB:
            pSubjInfo->dwUnionChoice    = MSSIP_ADDINFO_BLOB;
            if (!(pSubjInfo->psBlob = (MS_ADDINFO_BLOB *)pProvData->psPfns->pfnAlloc(sizeof(MS_ADDINFO_BLOB))))
            {
                pProvData->dwError = GetLastError();
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_SYSTEM_ERROR;
                return(FALSE);
            }

            memset(pSubjInfo->psBlob, 0x00, sizeof(MS_ADDINFO_BLOB));
            pSubjInfo->psBlob->cbStruct         = sizeof(MS_ADDINFO_BLOB);
            pSubjInfo->psBlob->cbMemObject      = pProvData->pWintrustData->pBlob->cbMemObject;
            pSubjInfo->psBlob->pbMemObject      = pProvData->pWintrustData->pBlob->pbMemObject;
            pSubjInfo->psBlob->cbMemSignedMsg   = pProvData->pWintrustData->pBlob->cbMemSignedMsg;
            pSubjInfo->psBlob->pbMemSignedMsg   = pProvData->pWintrustData->pBlob->pbMemSignedMsg;

            pSubjInfo->pwsDisplayName       = pProvData->pWintrustData->pBlob->pcwszDisplayName;
            break;

        case WTD_CHOICE_CATALOG:
          // The following APIs are in DELAYLOAD'ed mscat32.dll. If the
          // DELAYLOAD fails an exception is raised.
          __try {

            HANDLE                      hCatStore;
            MS_ADDINFO_CATALOGMEMBER    *pCatAdd;


            if (!(pSubjInfo->psCatMember))
            {
                if (!(pSubjInfo->psCatMember =
                    (MS_ADDINFO_CATALOGMEMBER *)pProvData->psPfns->pfnAlloc(sizeof(MS_ADDINFO_CATALOGMEMBER))))
                {
                    pProvData->dwError = GetLastError();
                    pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_SYSTEM_ERROR;
                    return(FALSE);
                }

                memset(pSubjInfo->psCatMember, 0x00, sizeof(MS_ADDINFO_CATALOGMEMBER));

                pSubjInfo->dwUnionChoice    = MSSIP_ADDINFO_CATMEMBER;

                pCatAdd                     = pSubjInfo->psCatMember;
                pCatAdd->cbStruct           = sizeof(MS_ADDINFO_CATALOGMEMBER);

                hCatStore = CryptCATOpen((WCHAR *)pProvData->pWintrustData->pCatalog->pcwszCatalogFilePath,
                                         CRYPTCAT_OPEN_EXISTING,
                                         pProvData->hProv,
                                         pProvData->pWintrustData->pCatalog->dwCatalogVersion,
                                         NULL);

                if (!(hCatStore) || (hCatStore == INVALID_HANDLE_VALUE))
                {
                    pProvData->padwTrustStepErrors[TRUSTERROR_STEP_CATALOGFILE]     = GetLastError();
                    pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV]   = CRYPT_E_FILE_ERROR;
                    return(FALSE);
                }

                pCatAdd->pStore     = CryptCATStoreFromHandle(hCatStore);
            }
            else
            {
                pCatAdd     = pSubjInfo->psCatMember;
                hCatStore   = CryptCATHandleFromStore(pCatAdd->pStore);
            }

            pCatAdd->pMember = NULL;

            if ( ( pProvData->pWintrustData->pCatalog->pbCalculatedFileHash != NULL ) &&
                 ( pProvData->pWintrustData->pCatalog->cbCalculatedFileHash != 0 ) )
            {
                LPWSTR pwszHashTag;

                if ( MsCatConstructHashTag(
                          pProvData->pWintrustData->pCatalog->cbCalculatedFileHash,
                          pProvData->pWintrustData->pCatalog->pbCalculatedFileHash,
                          &pwszHashTag
                          ) == TRUE )
                {
                    pCatAdd->pMember = CryptCATGetMemberInfo(hCatStore, pwszHashTag);
                    MsCatFreeHashTag(pwszHashTag);
                }
            }

            if (!(pCatAdd->pMember))
            {
                pCatAdd->pMember    = CryptCATGetMemberInfo(hCatStore,
                                             (WCHAR *)pProvData->pWintrustData->pCatalog->pcwszMemberTag);
            }

            if (!(pCatAdd->pMember))
            {
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_CATALOGFILE]     = GetLastError();
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV]   = TRUST_E_NOSIGNATURE;
                return(FALSE);
            }

            memcpy(&pProvData->pPDSip->gSubject, &pCatAdd->pMember->gSubjectType, sizeof(GUID));


            //
            //  assign the correct cert version so hashes will match if the file was already signed!
            //
            pSubjInfo->dwIntVersion = pCatAdd->pMember->dwCertVersion;

          } __except(EXCEPTION_EXECUTE_HANDLER) {
              pProvData->dwError = GetExceptionCode();
              pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] =
                  TRUST_E_SYSTEM_ERROR;
              return(FALSE);
          }
          break;
    }

    //
    //  set the GUID for the SIP...  this is done at the end because the pProvData member
    //  can get changed above!
    //
    memcpy(pSubjInfo->pgSubjectType, &pProvData->pPDSip->gSubject, sizeof(GUID));

    return(TRUE);
}

BOOL _GetMessage(CRYPT_PROVIDER_DATA *pProvData)
{
    DWORD               dwMsgEncoding;
    SIP_SUBJECTINFO     *pSubjInfo;
    SIP_DISPATCH_INFO   *pSip;

    DWORD               cbEncodedMsg;
    BYTE                *pbEncodedMsg;

    DWORD               dwMsgType;
    HCRYPTMSG           hMsg;
    HCRYPTPROV          hProv;

    dwMsgEncoding   = 0;
    dwMsgType       = 0;

    switch(pProvData->pWintrustData->dwUnionChoice)
    {
        case WTD_CHOICE_CATALOG:
            if ((_ISINSTRUCT(CRYPT_PROVIDER_DATA, pProvData->cbStruct, fRecallWithState)) &&
                (pProvData->fRecallWithState) &&
                (pProvData->hMsg))
            {
                return(TRUE);
            }

            pSip        = pProvData->pPDSip->pCATSip;
            pSubjInfo   = pProvData->pPDSip->psSipCATSubjectInfo;
            break;

        case WTD_CHOICE_BLOB:
        case WTD_CHOICE_FILE:
            pSip        = pProvData->pPDSip->pSip;
            pSubjInfo   = pProvData->pPDSip->psSipSubjectInfo;
            break;

        default:
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_NOSIGNATURE;
            return(FALSE);
    }

    cbEncodedMsg = 0;

    pSip->pfGet(pSubjInfo, &dwMsgEncoding, 0, &cbEncodedMsg, NULL);

    if (cbEncodedMsg == 0)
    {
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_SIP] = GetLastError();
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_NOSIGNATURE;
        return(FALSE);
    }

    if (!(pbEncodedMsg = (BYTE *)pProvData->psPfns->pfnAlloc(cbEncodedMsg)))
    {
        pProvData->dwError = GetLastError();
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_SYSTEM_ERROR;
        return(FALSE);
    }

    if (!(pSip->pfGet(pSubjInfo, &dwMsgEncoding, 0, &cbEncodedMsg, pbEncodedMsg)))
    {
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_SIP] = GetLastError();
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_NOSIGNATURE;

        pProvData->psPfns->pfnFree(pbEncodedMsg);

        return(FALSE);
    }

    pProvData->dwEncoding = dwMsgEncoding;

    if ((pProvData->dwEncoding & PKCS_7_ASN_ENCODING) &&
        (_NoContentWrap(pbEncodedMsg,  cbEncodedMsg)))
    {
        dwMsgType = CMSG_SIGNED;    // support for IE v3.0
    }

    // The default hProv to use depends on the type of the public key used to
    // do the signing.
    hProv = pProvData->hProv;
    if (hProv && hProv == I_CryptGetDefaultCryptProv(0))
        hProv = 0;

    if (!(hMsg = CryptMsgOpenToDecode(pProvData->dwEncoding, 0, dwMsgType,
                                      hProv, NULL, NULL)))
    {
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_MESSAGE] = GetLastError();
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = CRYPT_E_BAD_MSG;

        pProvData->psPfns->pfnFree(pbEncodedMsg);

        return(FALSE);
    }

    pProvData->hMsg = hMsg;

    // encoded message
    if (!(CryptMsgUpdate(hMsg, pbEncodedMsg, cbEncodedMsg, TRUE)))
    {
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_MESSAGE] = GetLastError();
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = CRYPT_E_BAD_MSG;

        pProvData->psPfns->pfnFree(pbEncodedMsg);

        return(FALSE);
    }

    pProvData->psPfns->pfnFree(pbEncodedMsg);

    return(TRUE);
}

BOOL _ExplodeMessage(CRYPT_PROVIDER_DATA *pProvData)
{
    DWORD               cbSize;
    DWORD               cbContent;
    BYTE                *pb;
    HCERTSTORE          hStore;

    if (!(_ISINSTRUCT(CRYPT_PROVIDER_DATA, pProvData->cbStruct, fRecallWithState)) ||
        !(pProvData->fRecallWithState))
    {
        // message cert store
        hStore = CertOpenStore(CERT_STORE_PROV_MSG,
                               pProvData->dwEncoding,
                               pProvData->hProv,
                               CERT_STORE_NO_CRYPT_RELEASE_FLAG,
                               pProvData->hMsg);
        if (hStore)
        {
            if (!(pProvData->psPfns->pfnAddStore2Chain(pProvData, hStore)))
            {
                pProvData->dwError = GetLastError();
                pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = TRUST_E_SYSTEM_ERROR;

                CertCloseStore(hStore, 0);

                return(FALSE);
            }

            CertCloseStore(hStore, 0);
        }
        else
        {
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = GetLastError();
            return(FALSE);
        }
    }

    // inner content type
    cbSize = 0;

    CryptMsgGetParam(pProvData->hMsg, CMSG_INNER_CONTENT_TYPE_PARAM, 0, NULL, &cbSize);

    if (cbSize == 0)
    {
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_SIGPROV] = CRYPT_E_BAD_MSG;
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_MSG_INNERCNTTYPE] = GetLastError();

        return(FALSE);
    }

    if (!(pb = (BYTE *)pProvData->psPfns->pfnAlloc(cbSize + 1)))
    {
        pProvData->dwError = GetLastError();
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_SIGPROV] = TRUST_E_SYSTEM_ERROR;

        return(FALSE);
    }

    if (!(CryptMsgGetParam(pProvData->hMsg, CMSG_INNER_CONTENT_TYPE_PARAM, 0,
                           pb, &cbSize)))
    {
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_MSG_INNERCNTTYPE] = GetLastError();
        pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_SIGPROV] = CRYPT_E_BAD_MSG;

        delete pb;

        return(FALSE);
    }

    pb[cbSize] = NULL;

    if (strcmp((char *)pb, SPC_INDIRECT_DATA_OBJID) == 0)
    {
        pProvData->psPfns->pfnFree(pb);

        cbContent = 0;

        CryptMsgGetParam(pProvData->hMsg, CMSG_CONTENT_PARAM, 0, NULL, &cbContent);

        if (cbContent == 0)
        {
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_SIGPROV] = CRYPT_E_BAD_MSG;
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_MSG_INNERCNT] = GetLastError();

            return(FALSE);
        }

        if (!(pb = (BYTE *)pProvData->psPfns->pfnAlloc(cbContent)))
        {
            pProvData->dwError = GetLastError();
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_SIGPROV] = TRUST_E_SYSTEM_ERROR;

            return(FALSE);
        }

        if (!(CryptMsgGetParam(pProvData->hMsg, CMSG_CONTENT_PARAM, 0,
                                pb, &cbContent)))
        {
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_SIGPROV] = CRYPT_E_BAD_MSG;
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_MSG_INNERCNT] = GetLastError();

            pProvData->psPfns->pfnFree(pb);

            return(FALSE);
        }

        if (!(TrustDecode(WVT_MODID_SOFTPUB, (BYTE **)&pProvData->pPDSip->psIndirectData, &cbSize, 202,
                          pProvData->dwEncoding, SPC_INDIRECT_DATA_CONTENT_STRUCT,
                          pb, cbContent, 0)))
        {
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_SIGPROV] = CRYPT_E_BAD_MSG;
            pProvData->padwTrustStepErrors[TRUSTERROR_STEP_MSG_INNERCNT] = GetLastError();

            pProvData->psPfns->pfnFree(pb);

            return(FALSE);
        }

        pProvData->psPfns->pfnFree(pb);
    }
    else
    {
        pProvData->psPfns->pfnFree(pb);

        if ((pProvData->pWintrustData->dwUnionChoice == WTD_CHOICE_CATALOG) &&
            (pProvData->pPDSip->psSipSubjectInfo->dwUnionChoice == MSSIP_ADDINFO_CATMEMBER))
        {
            //
            //  get the indirect data from the pMember!!!  Also, we want to
            //  allocate just the structure and copy the pointers over to it.
            //  this is so we can have a generic cleanup.
            //
            MS_ADDINFO_CATALOGMEMBER    *pCatAdd;

            pCatAdd = pProvData->pPDSip->psSipSubjectInfo->psCatMember;

            if ((pCatAdd) && (pCatAdd->pMember) && (pCatAdd->pMember->pIndirectData))
            {
                if (!(pProvData->pPDSip->psIndirectData =
                            (SIP_INDIRECT_DATA *)pProvData->psPfns->pfnAlloc(sizeof(SIP_INDIRECT_DATA))))
                {
                    pProvData->dwError = GetLastError();
                    pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_SIGPROV] = TRUST_E_SYSTEM_ERROR;

                    return(FALSE);
                }

                memcpy(pProvData->pPDSip->psIndirectData, pCatAdd->pMember->pIndirectData,
                                    sizeof(SIP_INDIRECT_DATA));
            }
        }
    }

    return(TRUE);
}

DWORD _SkipOverIdentifierAndLengthOctets(const BYTE *pbDER, DWORD cbDER)
{
#       define  TAG_MASK        0x1f

    DWORD           cb;
    DWORD           cbLength;
    const BYTE      *pb = pbDER;

    // Need minimum of 2 bytes
    if (cbDER < 2)
    {
        return(0);
    }

    // Skip over the identifier octet(s)
    if (TAG_MASK == (*pb++ & TAG_MASK))
    {
        // high-tag-number form
        for (cb=2; *pb++ & 0x80; cb++)
        {
            if (cb >= cbDER)
            {
                return(0);
            }
        }
    }
    else
    {
        // low-tag-number form
        cb = 1;
    }

    // need at least one more byte for length
    if (cb >= cbDER)
    {
        return(0);
    }

    if (0x80 == *pb)
    {
        // Indefinite
        cb++;
    }
    else if ((cbLength = *pb) & 0x80)
    {
        cbLength &= ~0x80;         // low 7 bits have number of bytes
        cb += cbLength + 1;

        if (cb > cbDER)
        {
            return(0);
        }
    }
    else
    {
        cb++;
    }

    return(cb);
}


BOOL _NoContentWrap(const BYTE *pbDER, DWORD cbDER)
{
    DWORD cb;

    cb = _SkipOverIdentifierAndLengthOctets(pbDER, cbDER);
    if ((cb > 0) && (cb < cbDER) && (pbDER[cb] == 0x02))
    {
        return TRUE;
    }

    return(FALSE);
}