/**@@@+++@@@@******************************************************************
**
** Microsoft Windows Media
** Copyright (C) Microsoft Corporation. All rights reserved.
**
***@@@---@@@@******************************************************************
*/

#include <drmcommon.h>
#include <drmutilities.h>
#include <drmcrt.h>
#include <drmcontextsizes.h>
#include <drmblackbox.h>
#include <drmlicenseparser.h>
#include <drmlicense.h>
#include <drmxmlparser.h>

#define RESTRICTED_COUNT    3
const DRM_BYTE CERT_SUBJECT_RESTRICTED[RESTRICTED_COUNT][__CB_DECL(SUBJ_LEN)] = 
{
    {   TWO_BYTES(0, 0), TWO_BYTES(0, 2)    }, 
    {   TWO_BYTES(0, 0), TWO_BYTES(0, 3)    }, 
    {   TWO_BYTES(0, 0), TWO_BYTES(0, 4)    }
};

static PUBKEY s_BrLicSrvPubl =
{
    {
        TWO_BYTES(0x34, 0x8b), TWO_BYTES(0x02, 0xae), TWO_BYTES(0xb6, 0x3a), TWO_BYTES(0x58, 0x8d),
        TWO_BYTES(0xb6, 0xf5), TWO_BYTES(0x38, 0x3b), TWO_BYTES(0x46, 0x47), TWO_BYTES(0xb2, 0xcd),
        TWO_BYTES(0xf5, 0x77), TWO_BYTES(0x67, 0x31), TWO_BYTES(0xaa, 0xe8), TWO_BYTES(0x33, 0x44),
        TWO_BYTES(0x44, 0xdb), TWO_BYTES(0x90, 0x95), TWO_BYTES(0xbb, 0x7f), TWO_BYTES(0xcc, 0x51),
        TWO_BYTES(0x80, 0x9d), TWO_BYTES(0x2d, 0x3a), TWO_BYTES(0x7f, 0xbd), TWO_BYTES(0x4c, 0x08)
    }
};

/******************************************************************************
** 
** Function :   DRM_LIC_GetAttribute
** 
** Synopsis :   Get an attribute from a license
** 
** Arguments :  pdstrLicense    - License string
**              pdstrAttrName   - Name of attribute
**              eAttribute      - Type of ettribute
**              pdstrLIData     - (Optional) LICENSORINFO\DATA section. If a 
**                                valid string, attribute will be searched 
**                                directly in this. On exit, the pointer will be 
**                                updated 
**              pdstrValue      - Value of attribute
**              chXPathSeparator- path separator to use
** 
** Returns :    
** 
** Notes :      
** 
******************************************************************************/
DRM_RESULT DRM_API DRM_LIC_GetAttribute(
    IN const DRM_CONST_STRING        *pdstrLicense, 
    IN const DRM_CONST_STRING        *pdstrAttrName,
    IN       eDRM_LICENSE_ATTRIBUTES  eAttribute,
    IN  OUT  DRM_CONST_STRING        *pdstrLIData,
        OUT  DRM_CONST_STRING        *pdstrValue,
    IN       DRM_WCHAR                chXPathSeparator)
{
    DRM_RESULT               dr = DRM_SUCCESS;    
    DRM_CONST_STRING        *pdstrSectionData   = NULL;    
    DRM_CONST_STRING         dstrSectionData    = EMPTY_DRM_STRING;
    DRM_CONST_STRING         dstrLicenseBody    = EMPTY_DRM_STRING;  
    const DRM_CONST_STRING  *pdstr              = NULL;

    ChkArg( pdstrLicense != NULL
         && pdstrValue   != NULL );
    
    if( chXPathSeparator == 0 )
    {
        chXPathSeparator = g_wchForwardSlash;
    }
    if ((eAttribute != DRM_LICENSE_ATTRIB_OTHER && 
         eAttribute != DRM_LICENSE_ATTRIB_REVOCATION &&
         eAttribute != DRM_LICENSE_ATTRIB_META_ATTRIBUTE ) && 
         NULL       != pdstrAttrName )
    {
        /* We allow user specified strings for META attributes, OTHER attribues and REVOCATION attributes */
        dr = DRM_E_INVALIDARG;
        goto ErrorExit;
    }

    if ( pdstrLIData == NULL )
    {
        pdstrSectionData = &dstrSectionData;
        ChkDR( DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrLIData, NULL, NULL, pdstrSectionData, NULL, g_wchForwardSlash ) );        
    }
    else
    {
        pdstrSectionData = pdstrLIData;
        if ( pdstrLIData->cchString == 0 )
        {
            ChkDR( DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrLIData, NULL, NULL, pdstrSectionData, NULL, g_wchForwardSlash ) );
        }
    }

    switch( eAttribute )
    {

        case DRM_LICENSE_ATTRIB_VERSION:        
            /* skip <?xml version="1.0"?>, if any */
            ChkDR(DRM_XML_GetNode(pdstrLicense, &g_dstrLicense, NULL, NULL, 0, &dstrLicenseBody, NULL));

            /* get the attr value */
            dr = DRM_XML_GetNodeAttribute( &dstrLicenseBody, &g_dstrAttributeVersion, pdstrValue);
            goto ErrorExit;

        case DRM_LICENSE_ATTRIB_KID:
            pdstr = &g_dstrTagKID;
            break;

        case DRM_LICENSE_ATTRIB_LID:
            pdstr = &g_dstrTagLID;
            break;
            
        case DRM_LICENSE_ATTRIB_METERING_ID:
            pdstr = &g_dstrTagMID; 
            break;
            
        case DRM_LICENSE_ATTRIB_REVOCATION:
            /* What type of revocation are they asking for? */
            /* We special case app revocation... */
            ChkArg( pdstrAttrName ); /* An attribute name is required if revocation lists are needed */
            if( DRM_UTL_DSTRStringsEqual( &g_dstrAppRevocation, pdstrAttrName ) )
            {
                pdstr = &g_dstrAppRevocation;
                pdstrAttrName = NULL;
            }
            else
            {
                pdstr = &g_dstrTagRevocation;
            }
            break;

        case DRM_LICENSE_ATTRIB_ISSUEDATE:
            pdstr = &g_dstrIssueDate;
            break;

        case DRM_LICENSE_ATTRIB_CONTENTPUBKEY:
            pdstr = &g_dstrContentPubKey;
            break;

        case DRM_LICENSE_ATTRIB_PRIORITY:
            pdstr = &g_dstrPriority;
            break;

        case DRM_LICENSE_ATTRIB_CHAINEDKID:
            pdstr = &g_dstrUplinkKid;
            break;

        case DRM_LICENSE_ATTRIB_META_ATTRIBUTE:        
            /* We are at the Data section.  We need to move to the META section */        
            ChkDR( DRM_XML_GetSubNodeByPath( pdstrSectionData, &g_dstrMeta, NULL, NULL, pdstrValue, NULL, g_wchForwardSlash ) );
            pdstrSectionData = pdstrValue;
            /* Fall through as META and regular attribute are the same.  Only difference is that we needed to be at a different
            node for META, which we are now. */

        case DRM_LICENSE_ATTRIB_OTHER:
            ChkArg( pdstrAttrName != NULL );
            pdstr = pdstrAttrName;
            pdstrAttrName = NULL;
            break;
            
        case DRM_LICENSE_ATTRIB_REVINFO:
            pdstr = &g_dstrTagRevInfoVersion;
            break;

        case DRM_LICENSE_ATTRIB_SOURCEID:
            pdstr = &g_dstrTagSourceID;
            break;
            
        default:
            ChkDR (DRM_E_INVALIDARG);
    }
    if( pdstrAttrName == NULL )
    {
        ChkDR( DRM_XML_GetSubNodeByPath( pdstrSectionData, pdstr, NULL, NULL, NULL, pdstrValue, chXPathSeparator ) );
    }
    else
    {
        ChkDR( DRM_XML_GetSubNodeByPath( pdstrSectionData, pdstr, &g_dstrAttributeType, pdstrAttrName, NULL, pdstrValue, chXPathSeparator ) );
    }

ErrorExit:
    return dr;
}

/******************************************************************************
** 
** Function :   DRM_LIC_GetEvent
** 
** Synopsis :   Parse and get details about an event from a license
** 
** Arguments :  pdstrLicense    - License string
**              pdstrEvent      - Name of event
**              pdstrTypeValue  - Type of event
**              pdstrLIData     - (Optional) LICENSORINFO\DATA section. If a 
**                                valid string, attribute will be searched 
**                                directly in this. On exit, the pointer will be 
**                                updated 
**              pdstrCondition  - Condition node from event
**              pdstrAction     - Action node from event
**              pdstrRestriction- Restriction node from event
** 
** Returns :    
** 
** Notes :      
** 
******************************************************************************/
DRM_RESULT DRM_API DRM_LIC_GetEvent(
    IN const DRM_CONST_STRING *pdstrLicense, 
    IN const DRM_CONST_STRING *pdstrEvent, 
    IN const DRM_CONST_STRING *pdstrTypeValue, 
    IN  OUT  DRM_CONST_STRING *pdstrLIData,
    OUT      DRM_CONST_STRING *pdstrCondition,     
    OUT      DRM_CONST_STRING *pdstrAction,
    OUT      DRM_CONST_STRING *pdstrRestriction)
{
    DRM_RESULT           dr                 = DRM_SUCCESS;
    DRM_CONST_STRING    *pdstrSectionData   = NULL;    
    DRM_CONST_STRING     dstrSectionData    = EMPTY_DRM_STRING;
    DRM_CONST_STRING     dstrListEvent      = EMPTY_DRM_STRING;

    if ( pdstrLIData == NULL)
    {
        pdstrSectionData = &dstrSectionData;
        ChkDR( DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrLIData, NULL, NULL, pdstrSectionData, NULL, g_wchForwardSlash ) );    
    }
    else
    {
        pdstrSectionData = pdstrLIData;
        if ( pdstrSectionData->cchString == 0)
        {
            pdstrSectionData = pdstrLIData;
            ChkDR( DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrLIData, NULL, NULL, pdstrSectionData, NULL, g_wchForwardSlash ) );    
        }
    }
    ChkDR( DRM_XML_GetSubNode( pdstrSectionData, 
                               pdstrEvent, 
                               pdstrTypeValue?&g_dstrAttributeType:NULL, 
                               pdstrTypeValue, 
                               0, 
                               &dstrListEvent, 
                               NULL, 
                               1 ) );
    if (NULL != pdstrCondition)
    {
        /* ONSTORE must use ACTION.CDATA not CONDITION.CDATA... */
        if( DRM_FAILED( DRM_XML_GetSubNodeByPath( &dstrListEvent, &g_dstrCondition, NULL, NULL, NULL, pdstrCondition, g_wchForwardSlash) ) )
        {
            INIT_DRM_STRING( *pdstrCondition );
        }
    }

    if (NULL != pdstrAction)
    {        
        if( DRM_FAILED( DRM_XML_GetSubNodeByPath( &dstrListEvent, &g_dstrAction, NULL, NULL, NULL, pdstrAction, g_wchForwardSlash) ) )
        {          
            INIT_DRM_STRING( *pdstrAction );
        }
    }

    if (NULL != pdstrRestriction)
    {  
        if( DRM_FAILED( DRM_XML_GetSubNode( &dstrListEvent, 
                                            &g_dstrRestrictions, 
                                            NULL, 
                                            NULL, 
                                            0,
                                            NULL, 
                                            pdstrRestriction, 
                                            1 ) ) )
        {
            INIT_DRM_STRING( *pdstrRestriction );            
        }        
    }

    dr = DRM_SUCCESS;
ErrorExit:
    return(dr);
}


/*--------------------------------------------------------------------
** 
** Function :   DRM_LIC_GetNextActionEvent
** 
** Synopsis :   
** 
** Arguments :  [pdstrActionsBuffer] - XML Buffer holding the ONACTION events
**              [pdstrCondition] - Condition inside ONACTION\CONDITION\CDATA
**
** Returns :    On returning, pdstrCondition contains the condition to be 
**              evaluated, and pdstrActionsBuffer is advanced beyond the current
**              ONACTION node, if there was one.
**              returns DRM_E_XMLNOTFOUND when no more ONACTION events are 
**              present
** 
** Notes :      
** 
--------------------------------------------------------------------*/
DRM_RESULT DRM_API DRM_LIC_GetNextActionEvent(
    IN OUT      DRM_CONST_STRING *pdstrActionsBuffer, 
    OUT         DRM_CONST_STRING *pdstrCondition )
{
    DRM_RESULT          dr = DRM_SUCCESS;
    DRM_CONST_STRING    dstrAction = EMPTY_DRM_STRING;
    
    /*  Check input */
    ChkDRMString(pdstrActionsBuffer);
    ChkArg( pdstrCondition != NULL );    

    /*  Initialize output   */
    pdstrCondition->cchString  = 0;
    pdstrCondition->pwszString = NULL;

    /*  Get the next action  */
    ChkDR(DRM_XML_GetSubNode( 
                pdstrActionsBuffer, 
                &g_dstrLicEvalOnAction, 
                NULL, 
                NULL, 
                0, 
                &dstrAction, 
                NULL,
                0));

    /*  Advance Action buffer to the next action    */
    pdstrActionsBuffer->pwszString += dstrAction.cchString;
    pdstrActionsBuffer->cchString  -= dstrAction.cchString;
    
    /*  Get the condition  */
    dr = DRM_XML_GetSubNodeByPath( 
            &dstrAction, 
            &g_dstrCondition, 
            NULL, 
            NULL, 
            NULL, 
            pdstrCondition, 
            g_wchForwardSlash);
    if( DRM_FAILED( dr ) )
    { 
        pdstrCondition->cchString  = 0;
        pdstrCondition->pwszString = NULL;
        ChkDR(dr);
    }      
    
ErrorExit:
    return(dr);
}

static DRM_RESULT GetLSPubKey(
    const DRM_CONST_STRING   *pdstrLicense, 
          PUBKEY             *pbPubKey, 
          DRM_CRYPTO_CONTEXT *pcontextCrypto)
{
    DRM_RESULT       dr = DRM_SUCCESS;
    DRM_CONST_STRING dstrCert          = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrListCertChain = EMPTY_DRM_STRING;
    DRM_DWORD        iNode = 0;
    
    if (pbPubKey == NULL)
    {
        dr = LIC_PARAM_NOT_OPTIONAL;
        goto ErrorExit;
    }
        
    ChkDR( DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrCertChain, NULL, NULL, &dstrListCertChain, NULL, g_wchForwardSlash ) );
    while( DRM_SUCCEEDED( DRM_XML_GetSubNode( &dstrListCertChain, &g_dstrTagCertificate, NULL, NULL, iNode, &dstrCert, NULL, 1 ) ) )
    {
        iNode++;
    }
    if( iNode == 0 )
    {
        dr = LIC_INVALID_LICENSE;
        goto ErrorExit;
    }

    ChkDR( DRM_XML_GetSubNode( &dstrListCertChain, &g_dstrTagCertificate, NULL, NULL, iNode - 1, NULL, &dstrCert, 1 ) );
    iNode = SIZEOF( CERT );
    ChkDR(DRM_B64_DecodeW(&dstrCert, &iNode, (DRM_BYTE *)&(pcontextCrypto->union_cert.cert), 0 ) );
    MEMCPY( pbPubKey, &(pcontextCrypto->union_cert.cert.cd.pk), SIZEOF(PUBKEY) );    

ErrorExit:
    if( DRM_FAILED( dr ) )
    {
        dr = LIC_INVALID_LICENSE;
    }

    return(dr);
}

DRM_RESULT DRM_API DRM_LIC_GetEnablingBits(
    IN const DRM_CONST_STRING   *pdstrLicense,
    IN       DRM_DWORD           dwIndex,
    OUT      DRM_DWORD          *pdwAlgorithm,
    OUT      PUBKEY             *pPubKey, 
    OUT      DRM_BYTE           *pbValue, 
    OUT      DRM_DWORD          *pdValueLen, 
    OUT      PUBKEY             *pVerPubKey, 
    OUT      DRM_BYTE           *pbSignature,
    OUT      DRM_BYTE            rgbChainedChecksum[CHECKSUM_LENGTH],
    IN       DRM_CRYPTO_CONTEXT *pcontextCrypto)
{
    DRM_RESULT       dr = DRM_SUCCESS;
    DRM_CONST_STRING dstrString              = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrEnablingBitsSection = EMPTY_DRM_STRING;
    DRM_DWORD        dwSize                  = SIZEOF( PUBKEY );
    DRM_BOOL         fSymmetricallyBound     = FALSE;

    if ( pPubKey     == NULL 
      || pdValueLen  == NULL 
      || pbSignature == NULL )
    {
        dr = LIC_PARAM_NOT_OPTIONAL;
        goto ErrorExit;
    }

    dr = DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrDrmRestoreInfoEnablingBits, NULL, NULL, &dstrEnablingBitsSection, NULL, g_wchForwardSlash );
    if( DRM_FAILED( dr ) 
     && dwIndex == 0 )
    {
        /* There is no restore enabling bits.  So 0 index is the first regular enabling bits */
        dwIndex++;
    }

    if( dwIndex > 0 )
    {
        ChkDR( DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrLIData, NULL, NULL, &dstrString, NULL, g_wchForwardSlash ) );
        dr = DRM_XML_GetSubNode( &dstrString, &g_dstrTagEnablingbits, NULL, NULL, dwIndex - 1, &dstrEnablingBitsSection, NULL, 1);
        if( DRM_FAILED( dr ) )
        {
            /* These was no ENABLINGBITS section.  This could be a chained license.  Look for CHAINEDENABLINGBITS */
            ChkDR( DRM_XML_GetSubNode( &dstrString, &g_dstrChainedEnablingBits, NULL, NULL, dwIndex - 1, &dstrEnablingBitsSection, NULL, 1) );
            if( NULL != rgbChainedChecksum )
            {
                /* Get PubKey     */
                ChkDR( DRM_XML_GetSubNodeByPath( &dstrEnablingBitsSection, &g_dstrChainedCheckSum, NULL, NULL, NULL, &dstrString, g_wchForwardSlash ) );
                /* Decode PubKey */
                dwSize = CHECKSUM_LENGTH;
                ChkDR(DRM_B64_DecodeW(&dstrString, &dwSize, rgbChainedChecksum, 0) );
            }

        }
        else
        {
            ChkDR( DRM_XML_GetSubNodeByPath( &dstrEnablingBitsSection, &g_dstrTagPubkey, NULL, NULL, NULL, &dstrString, g_wchForwardSlash ) );
            /* Decode PubKey */
            dwSize = SIZEOF( PUBKEY );
            ChkDR( DRM_B64_DecodeW(&dstrString, &dwSize, (DRM_BYTE *)pPubKey, 0) );
        }

#if DRM_SUPPORT_SYMMETRIC_OPTIMIZATIONS
        /* Get PubKey     */
        if( DRM_SUCCEEDED( DRM_XML_GetSubNode(  pdstrLicense,
                                               &g_dstrTagSymValue,
                                                NULL,
                                                NULL,
                                                0,
                                               &dstrString,
                                                NULL,
                                                2 ) ) )
        {
            /* This license has been rebound to a symmetric key.  Ensure that we return the correct values in this case */
            fSymmetricallyBound = TRUE;
        }
#endif

        if ( NULL != pVerPubKey )
        {
            ChkDR( GetLSPubKey( pdstrLicense, pVerPubKey, pcontextCrypto ) );
        }
    }
    else if( NULL != pVerPubKey )
    {
        /* Get PubKey     */
        ChkDR( DRM_XML_GetSubNodeByPath( &dstrEnablingBitsSection, &g_dstrTagPubkey, NULL, NULL, NULL, &dstrString, g_wchForwardSlash ) );
        /* Decode PubKey */
        dwSize = SIZEOF( PUBKEY );
        ChkDR(DRM_B64_DecodeW(&dstrString, &dwSize, (DRM_BYTE *)pPubKey, 0) );
        MEMCPY(pVerPubKey, &s_BrLicSrvPubl, SIZEOF(PUBKEY) );
    }

    if (NULL != pdwAlgorithm )
    {
        if( fSymmetricallyBound )
        {
            *pdwAlgorithm = eMSDRM_SYM;
        }
        else
        {
            DRM_CONST_STRING dstrAlgo = EMPTY_DRM_STRING;

            *pdwAlgorithm = 0;

            ChkDR( DRM_XML_GetSubNodeByPath( &dstrEnablingBitsSection, &g_dstrTagHashAlgorithm, NULL, NULL, &dstrString, NULL, g_wchForwardSlash ) );
            ChkDR( DRM_XML_GetNodeAttribute( &dstrString, &g_dstrAttributeType, &dstrAlgo ) );
            if( DRM_UTL_DSTRStringsEqual( &dstrAlgo, &g_dstrMSDRM ) )
            {
                *pdwAlgorithm = eMSDRM_PK;
                /* Is is a eMSDRM_PK algorithm */
            }
            else if( DRM_UTL_DSTRStringsEqual( &dstrAlgo, &g_dstrMSDRM_CK ) )
            {
                *pdwAlgorithm = eMSDRM_CK;
                /* It is a eMSDRM_CK algorithm */
            }
            else
            {
                ChkDR( DRM_E_UNSUPPORTEDALGORITHM );
            }
        }
    }

    /* Get Value     */
    if( fSymmetricallyBound )
    {
        ChkDR( DRM_XML_GetSubNode( pdstrLicense, &g_dstrTagSymValue, NULL, NULL, 0, NULL, &dstrString, 2) );
    }
    else
    {
        ChkDR( DRM_XML_GetSubNodeByPath( &dstrEnablingBitsSection, &g_dstrTagValue, NULL, NULL, NULL, &dstrString, g_wchForwardSlash ) );
    }
    
    /* Base64 Decode Value */
    ChkDR(DRM_B64_DecodeW(&dstrString, pdValueLen, pbValue, 0 ) );

    /* Get Signature     */
    ChkDR( DRM_XML_GetSubNodeByPath( &dstrEnablingBitsSection, &g_dstrTagSignature, NULL, NULL, NULL, &dstrString, g_wchForwardSlash ) );

    /* Decode Signature */
    dwSize = PK_ENC_SIGNATURE_LEN;
    ChkDR(DRM_B64_DecodeW(&dstrString, &dwSize, pbSignature, 0) );

    dr = DRM_SUCCESS;
ErrorExit:
    return(dr);
}

DRM_RESULT DRM_API DRM_LIC_VerifySignature(
    IN const DRM_CONST_STRING *pdstrLicense,
    IN       DRM_BB_CONTEXT   *pcontextBBX,
       OUT   DRM_LONG         *plResult)
{
    DRM_RESULT       dr  = DRM_SUCCESS;    
    DRM_CONST_STRING dstrData      = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrLicInfo   = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrSignature = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrLID       = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrBRInfo    = EMPTY_DRM_STRING;
    DRM_DWORD        dwSize = PK_ENC_SIGNATURE_LEN;
    
    ChkArg ( plResult     != NULL 
          && pdstrLicense != NULL
          && pcontextBBX  != NULL );

    *plResult = 0; /* Init. */

    ChkDR( GetLSPubKey(pdstrLicense, &pcontextBBX->CryptoContext.pubKey, &pcontextBBX->CryptoContext ) );
    
    /* Get DATA section     */
    ChkDR( DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrLIData, NULL, NULL, &dstrData, NULL, g_wchForwardSlash ) );

#if DRM_SUPPORT_SYMMETRIC_OPTIMIZATIONS
    /* Look for a SYMMETRIC signature first.  If not found then proceed to regular signature verification */
    if( DRM_SUCCEEDED( DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrLicensorInfoNode, NULL, NULL, &dstrLicInfo, NULL, g_wchForwardSlash ) ) )
    {
        if( DRM_SUCCEEDED( DRM_XML_GetSubNode( &dstrLicInfo,
                                               &g_dstrTagSymSig,
                                                NULL,
                                                NULL,
                                                0,
                                                NULL,
                                                &dstrSignature,
                                                1 ) ) )
        {
            /* There is a symmetric signature, verify it. */
            DRM_BYTE rgbSymSig[__CB_DECL( SHA_DIGEST_LEN )];
            
            dwSize = SIZEOF( rgbSymSig );
            ChkDR( DRM_B64_DecodeW( &dstrSignature, &dwSize, rgbSymSig, 0 ) );
            ChkDR( DRM_BBX_SymmetricVerify( pcontextBBX, 
                                            PB_DSTR( &dstrData ), 
                                            CB_DSTR( &dstrData ), 
                                            NULL,
                                            rgbSymSig ) );

            /* Signture verification passed.  Skip the rest of the function */
            *plResult = 1;
            goto ErrorExit;
        }
    }
#endif


    /* Get the LID */
    ChkDR( DRM_XML_GetSubNodeByPath( &dstrData, &g_dstrTagLID, NULL, NULL, &dstrLID, NULL, g_wchForwardSlash ) );

    if( DRM_FAILED( DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrLISigHashAlgo, &g_dstrAttributeType, &g_dstrSHA, &dstrSignature, NULL, g_wchForwardSlash ) ) )
    {
        dr = LIC_UNSUPPORTED_VALUE;
        goto ErrorExit;
    }


    if( DRM_FAILED( DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrLISigAlgo, &g_dstrAttributeType, &g_dstrMSDRM, &dstrSignature, NULL, g_wchForwardSlash ) ) )
    {
        dr = LIC_UNSUPPORTED_VALUE;
        goto ErrorExit;
    }
    
    ChkDR( DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrLISigValue, NULL, NULL, NULL, &dstrSignature, g_wchForwardSlash ) );
    dwSize = PK_ENC_SIGNATURE_LEN;
    ChkDR(DRM_B64_DecodeW(&dstrSignature, &dwSize, pcontextBBX->CryptoContext.signature, 0 ) );

    if ( DRM_PK_Verify( pcontextBBX->CryptoContext.rgbCryptoContext, &pcontextBBX->CryptoContext.pubKey, PB_DSTR(&dstrData), CB_DSTR(&dstrData), pcontextBBX->CryptoContext.signature) )
    {
        if( DRM_SUCCEEDED( DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrDrmRestoreInfo, NULL, NULL, &dstrBRInfo, NULL, g_wchForwardSlash ) ) )
        {
            /* The DRMRESTOREINFO section exists so we need to validate that             */
            
            
            ChkDR( DRM_XML_GetSubNodeByPath( &dstrBRInfo, &g_dstrTagSignature, NULL, NULL, &dstrData, NULL, g_wchForwardSlash ) );
            
            /* 
            ** Get the SHA hashalgorithm and MSDRM signalgorithm.  
            ** Don't do anything with it just make sure it is there
            */
            ChkDR( DRM_XML_GetSubNode( &dstrData, &g_dstrTagHashAlg, &g_dstrAttributeType, &g_dstrSHA,   0, &dstrSignature, NULL, 1 ) );
            ChkDR( DRM_XML_GetSubNode( &dstrData, &g_dstrTagSignAlg, &g_dstrAttributeType, &g_dstrMSDRM, 0, &dstrSignature, NULL, 1 ) );

            ChkDR( DRM_XML_GetSubNodeByPath( &dstrBRInfo, &g_dstrXPathSigValue, NULL, NULL, NULL,       &dstrSignature, g_wchForwardSlash ) );            
            ChkDR( DRM_XML_GetSubNodeByPath( &dstrBRInfo, &g_dstrTagData,       NULL, NULL, &dstrData,   NULL,          g_wchForwardSlash ) );

            /* Get the LID that is in the DRMRESTOREINFO/DATA section */
            ChkDR( DRM_XML_GetSubNodeByPath( &dstrData,   &g_dstrTagLID,        NULL, NULL, &dstrBRInfo, NULL,          g_wchForwardSlash ) );

            /* Compare with the original LID to prevent a spoofing attack */
            if( !DRM_UTL_DSTRStringsEqual( &dstrBRInfo, &dstrLID ) )
            {
                /* The LID from the original license and the LID in the B/R info don't match */
                /* Someone isn't playing nice.  We fail the signature check because something is fishy. */
                dr = LIC_INVALID_LICENSE;
                goto ErrorExit;
            }
            dwSize = PK_ENC_SIGNATURE_LEN;
            ChkDR(DRM_B64_DecodeW(&dstrSignature, &dwSize, pcontextBBX->CryptoContext.signature, 0) );

            if( dstrData.cchString > ((DRM_DWORD)(-1)) ||
                dstrData.cchString * SIZEOF( DRM_WCHAR ) > ((DRM_DWORD)(-1)) )
            {
                dr = DRM_E_ARITHMETIC_OVERFLOW;
                goto ErrorExit;
            }

            if ( DRM_PK_Verify( pcontextBBX->CryptoContext.rgbCryptoContext, &s_BrLicSrvPubl, PB_DSTR(&dstrData), CB_DSTR(&dstrData), pcontextBBX->CryptoContext.signature) )                
            {
                *plResult = 1;
            }

        }
        else
        {
            *plResult = 1;
        }
    }
    dr = DRM_SUCCESS;
ErrorExit:
    return( dr );
}

DRM_RESULT DRM_API DRM_LIC_VerifyCertChain(
    IN const DRM_CONST_STRING    *pdstrLicense, 
    IN       DRM_BOOL             fCheckExpiry,
    IN       DRM_LICEVAL_CONTEXT *pcontextLEVL,
    OUT      DRM_LONG            *plResult)
{
    DRM_RESULT       dr = DRM_SUCCESS;    
    DRM_CONST_STRING dstrCert          = EMPTY_DRM_STRING; 
    DRM_CONST_STRING dstrListCertChain = EMPTY_DRM_STRING;
    DRM_DWORD        dwIndex = 0;
    DRM_BOOL         fFirstCertCheck = TRUE;

    ChkArg( plResult     != NULL
         && pdstrLicense != NULL
         && pcontextLEVL != NULL );

    *plResult = 0;
    
    ChkDR( DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrCertChain, NULL, NULL, &dstrListCertChain, NULL, g_wchForwardSlash ) );

    for(dwIndex = 0; DRM_SUCCEEDED(dr); dwIndex++ )
    {
        DRM_DWORD dwSize = SIZEOF( CERT );
        dr = DRM_XML_GetSubNode( &dstrListCertChain, 
                                 &g_dstrTagCertificate, 
                                 NULL, 
                                 NULL, 
                                 dwIndex, 
                                 NULL, 
                                 &dstrCert, 
                                 1 );
        if ( (dr == DRM_E_XMLNOTFOUND) && (dwIndex == 0) )
        {
            ChkDR( LIC_INVALID_LICENSE );
        }
        else if ( DRM_SUCCEEDED(dr) )
        {                    
            ChkDR(DRM_B64_DecodeW(&dstrCert, 
                                  &dwSize, 
                                  (DRM_BYTE*) &(pcontextLEVL->pcontextBBX->CryptoContext.union_cert.cert), 
                                  0));
            ChkDR( DRM_UTL_CheckCert( &(pcontextLEVL->pcontextBBX->CryptoContext.union_cert.cert), 
                                      fFirstCertCheck?NULL:&(pcontextLEVL->pcontextBBX->CryptoContext.pubKey),
                                      fCheckExpiry, 
                                      pcontextLEVL ) );
            MEMCPY( &(pcontextLEVL->pcontextBBX->CryptoContext.pubKey), 
                    &(pcontextLEVL->pcontextBBX->CryptoContext.union_cert.cert.cd.pk), 
                    SIZEOF( PUBKEY ) );
            fFirstCertCheck = FALSE;   
        }
    }

    *plResult = 1;
    dr = DRM_SUCCESS;

ErrorExit:
    return(dr);
}

#if DRM_SUPPORT_CONTENT_REVOCATION

DRM_RESULT DRM_API DRM_LIC_VerifyContentRevocation(
    IN const DRM_CONST_STRING   *pdstrContentRevocation, 
    OUT      DRM_CONST_STRING   *pdstrLSPubKey,
    OUT      DRM_DWORD          *pdwSequenceNumber,
    OUT      DRM_CONST_STRING   *pdstrContentOwnerPubKey,
    OUT      DRM_CONST_STRING   *pdstrCondition,
    IN       DRM_CRYPTO_CONTEXT *pcontextCrypto)
{
    DRM_RESULT       dr = DRM_E_LOGICERR;
    DRM_CONST_STRING dstrData          = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrContentPubKey = EMPTY_DRM_STRING;
    DRM_DWORD        dwSize = 0;

    ChkArg( pcontextCrypto         != NULL
         && pdstrContentRevocation != NULL );
        
    /* Initialize output parameters. */
    if (NULL != pdwSequenceNumber)
    {
        *pdwSequenceNumber = 0;
    }
    if (NULL != pdstrContentOwnerPubKey)
    {
        INIT_DRM_STRING( *pdstrContentOwnerPubKey );
    }
    if (NULL != pdstrCondition)
    {
        INIT_DRM_STRING( *pdstrCondition );
    }
        
    ChkDR( DRM_XML_GetSubNodeByPath( pdstrContentRevocation, &g_dstrTagData, NULL, NULL, &dstrData, NULL, g_wchForwardSlash ) );

    /* Get Sequence Number */
    if (NULL != pdwSequenceNumber)
    {
        DRM_CONST_STRING dstrSequenceNumber = EMPTY_DRM_STRING;        
        dr = DRM_XML_GetSubNodeByPath( &dstrData, &g_dstrSequenceNumber, NULL, NULL, NULL, &dstrSequenceNumber, g_wchForwardSlash );
        if( DRM_SUCCEEDED( dr ) )
        {
            ChkDR (wcsntol (dstrSequenceNumber.pwszString, 
                            dstrSequenceNumber.cchString, 
               (DRM_LONG *) pdwSequenceNumber));
        }
    }

    /* Get Content Owner PubKey     */
    ChkDR( DRM_XML_GetSubNodeByPath( &dstrData, &g_dstrContentPubKey, NULL, NULL, NULL, &dstrContentPubKey, g_wchForwardSlash ) );
    if (NULL != pdstrContentOwnerPubKey)
    {
        ASSIGN_DRM_STRING( *pdstrContentOwnerPubKey, dstrContentPubKey );
    }
    dwSize = SIZEOF( PUBKEY );
    ChkDR(DRM_B64_DecodeW(&dstrContentPubKey, &dwSize, (DRM_BYTE *)&(pcontextCrypto->pubKey), 0 ) );

    /* Get License Server PubKey. It is optional. */
    if (NULL != pdstrLSPubKey)
    {        
        dr = DRM_XML_GetSubNodeByPath( &dstrData, &g_dstrLSPubKey, NULL, NULL, NULL, pdstrLSPubKey, g_wchForwardSlash );
        if( DRM_FAILED( dr ) )
        {
            INIT_DRM_STRING( *pdstrLSPubKey );
        }
    }

    /* Get the Condition */
    if (NULL != pdstrCondition)
    {
        if( DRM_FAILED( DRM_XML_GetSubNodeByPath( &dstrData, &g_dstrCondition, NULL, NULL, NULL, pdstrCondition, g_wchForwardSlash) ) )
        {
            INIT_DRM_STRING( *pdstrCondition );
        }
    }
    
    ChkDR( DRM_XML_GetSubNodeByPath( pdstrContentRevocation, &g_dstrTagSignature, NULL, NULL, &dstrContentPubKey, NULL, g_wchForwardSlash ) );

    /* Get the SHA hashalgorithm.  Don't do anything with it just make sure it is there */
    ChkDR( DRM_XML_GetSubNode( &dstrContentPubKey, &g_dstrTagHashAlg, &g_dstrAttributeType, &g_dstrSHA, NULL, &dstrData, NULL, 1 ) );

    /* Get the MSDRM signalgorithm.  Don't do anything with it just make sure it is there */
    ChkDR( DRM_XML_GetSubNode( &dstrContentPubKey, &g_dstrTagSignAlg, &g_dstrAttributeType, &g_dstrMSDRM, NULL, &dstrData, NULL, 1 ) );

    ChkDR( DRM_XML_GetSubNodeByPath( pdstrContentRevocation, &g_dstrXPathSigValue, NULL, NULL, NULL, &dstrData, g_wchForwardSlash ) );
    dwSize = PK_ENC_SIGNATURE_LEN;
    ChkDR(DRM_B64_DecodeW(&dstrData, &dwSize, pcontextCrypto->signature, 0) );
    
    ChkDR( DRM_XML_GetSubNodeByPath( pdstrContentRevocation, &g_dstrTagData, NULL, NULL, &dstrData, NULL, g_wchForwardSlash ) );

    if( dstrData.cchString > ((DRM_DWORD)(-1)) ||
        dstrData.cchString * SIZEOF( DRM_WCHAR ) > ((DRM_DWORD)(-1)) )
    {
        ChkDR( DRM_E_ARITHMETIC_OVERFLOW );
    }

    /* Use PkCrypto to verify the signature of the DATA section        */
    if (!DRM_PK_Verify( pcontextCrypto->rgbCryptoContext, 
                       &pcontextCrypto->pubKey,
                        PB_DSTR( &dstrData ), 
                        CB_DSTR( &dstrData ),
                        pcontextCrypto->signature) )
    {
        /* This is internally used to indicate that the content revocation blob is bad. */
        ChkDR( DRM_E_INVALID_SIGNATURE );
    }
    
    /* Everything looks good.  */
    dr = DRM_SUCCESS;
ErrorExit:
    return ( dr );    
}


DRM_RESULT DRM_API DRM_LIC_GetContentRevocation(
    IN const DRM_CONST_STRING   *pdstrLicense,
    IN       DRM_DWORD           dwSequenceNumber,
    OUT      DRM_DWORD          *pdwSequenceNumber,
    OUT      DRM_CONST_STRING   *pdstrContentRevocation,
    OUT      DRM_CONST_STRING   *pdstrContentOwnerPubKey,
    OUT      DRM_CONST_STRING   *pdstrCondition,
    IN       DRM_CRYPTO_CONTEXT *pcontextCrypto)
{
    DRM_RESULT       dr = DRM_SUCCESS;
    DRM_CONST_STRING dstrSectionData = EMPTY_DRM_STRING;    
    DRM_CONST_STRING dstrLSPubKey    = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrTemp        = EMPTY_DRM_STRING;

    ChkArg( pdstrLicense            != NULL
         && pdstrContentRevocation  != NULL
         && pdstrContentOwnerPubKey != NULL );
        
    if( DRM_FAILED ( DRM_XML_GetSubNodeByPath( pdstrLicense, &g_dstrLIData, NULL, NULL, &dstrSectionData, NULL, g_wchForwardSlash ) ) )
    {
        dr = DRM_S_FALSE;
        goto ErrorExit;
    }
    
    if( DRM_FAILED ( DRM_XML_GetSubNode( &dstrSectionData, &g_dstrContentRevocation, NULL, NULL, dwSequenceNumber, pdstrContentRevocation, &dstrTemp, 1 ) ) )
    {
        dr = DRM_S_FALSE;
        goto ErrorExit;
    }

    /* Verify and set output parameters. */
    ChkDR( DRM_LIC_VerifyContentRevocation( pdstrContentRevocation, &dstrLSPubKey, pdwSequenceNumber, pdstrContentOwnerPubKey, pdstrCondition, pcontextCrypto) );
    
    /* If license server pubkey is in the content revocation list, make sure that it corresponds to that of this license. */
    /* If the license server pubkey exists and is non-empty string, make sure it matched the one in license. */
    if ( NULL != dstrLSPubKey.pwszString )
    {
        DRM_DWORD dwSize = SIZEOF( PUBKEY );

        /* Retrieve pubkey of license server for this license. */
        ChkDR( GetLSPubKey( pdstrLicense, &(pcontextCrypto->pubKey), pcontextCrypto ) );
        ChkDR(DRM_B64_DecodeW(&dstrLSPubKey, &dwSize, (DRM_BYTE*) &(pcontextCrypto->pubKey2), 0));
        if (MEMCMP((DRM_BYTE*) &(pcontextCrypto->pubKey), (DRM_BYTE*) &(pcontextCrypto->pubKey2), SIZEOF( PUBKEY ) ) != 0 )
        {
            ChkDR( DRM_E_KEY_MISMATCH );
        }
    }
    dr = DRM_SUCCESS;
ErrorExit:
    return(dr);
}
#endif

