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

#include <drmcommon.h>
#include <drmutilities.h>
#include <drmcontextsizes.h>
#include <drmpkcrypto.h>
#include <drmcipher.h>
#include <drmsha1.h>
#include <drmsecurestore.h>
#include <drmrevocation.h>
#include <drmlicenseparser.h>
#include <drmxmlparser.h>
#include <drmxmlutilities.h>
#include <drmcrt.h>
#include <oemimpl.h>
#include <byteorder.h>
#include <drmint64.h>
#include <drmcert.h>
#include <drmbase64.h>
#include <drmcertparser.h>
#include <drmrsaex.h>


/* keypair for device exclusion; the private key is cloaked and must be uncloaked to compile */

#include <drmpubkeydeviceexclusion.h>

/* pubkey for app revocation */

#include <drmpubkeyrevocation.h>

#if DRM_SUPPORT_WMDRMNET
static DRM_RESULT DRM_API _VerifyBinaryWMDRMNETSignature(
    IN     DRM_BYTE  *f_pbBinary,
    IN     DRM_DWORD  f_cbBinary,
    IN OUT DRM_DWORD *f_pidSerial);

static DRM_RESULT _VerifyWMDRMNETRevocationList(
    IN  DRM_CONST_STRING   *f_pdstrList,
    OUT DRM_DWORD          *f_pdwVersion );
#endif

DRM_RESULT
_ValidateCertificate(
    IN DRM_CHAR          *f_rgchBase,
    IN DRM_SUBSTRING      f_dasstrCertificate,
    IN OUT DRM_SUBSTRING *f_pdasstrWMDRMNDCertificate);

DRM_RESULT _UpdateAppRevocationList(
    DRM_CRYPTO_CONTEXT   *f_pcontextCRY,
    DRM_SECSTORE_CONTEXT *f_pcontextSST,
    DRM_BYTE              f_rgbPassword [__CB_DECL(SHA_DIGEST_LEN)],
    DRM_BYTE             *f_pbRevocationList,
    DRM_DWORD             f_cbRevocationList,
    DRM_HDS_CONTEXT      *f_pcontextHDS,
    APPCERT              *f_pappcert,
    DRM_DWORD             f_idCRLAppCurrent,
    DRM_BOOL             *f_pfUpdatedAppRevocationList);

/*****************************************************************************
** Function: _CreateRevocationStorePassword
**
** Synopsis: create the one-byte-zero hash to open the revocations secure store
**
** Arguments:
**            [f_pcontextBBX] -- blackbox context
**            [f_rgbPassword] -- output buffer
*****************************************************************************/

DRM_RESULT DRM_API _CreateRevocationStorePassword(
    IN  DRM_BB_CONTEXT *f_pcontextBBX,
    OUT DRM_BYTE        f_rgbPassword [__CB_DECL(SHA_DIGEST_LEN)])
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_BYTE bZero = 0x00;
    static DRM_BOOL fInitedPassword = FALSE;
    static DRM_BYTE rgbPassword [__CB_DECL(SHA_DIGEST_LEN)];

    if( !fInitedPassword )
    {
        /* If we've never calculated the password before, do it now */
        ChkDR( DRM_BBX_HashValue(&bZero, SIZEOF (bZero), rgbPassword, f_pcontextBBX) );
        fInitedPassword = TRUE;
    }

    /* Copy the cached password, it will never change. */
    MEMCPY( f_rgbPassword, rgbPassword, SHA_DIGEST_LEN );

ErrorExit:
    return dr;
}

#if DRM_SUPPORT_APP_REVOCATION
/************************ APP REVOCATION ************************/

const DRM_DWORD CURRENT_REVOCATION_VERSION = 2;

typedef DRM_DWORD DRM_CERT_TYPE;
#define CERTTYPE_V1APP 0

typedef struct __tagDRM_REVOCATION_LIST_ELEMENT
{
    DRM_CERT_TYPE certificatetype;
    DRM_DWORD     cbCert;
    DRM_BYTE      rgbCert [1];
}DRM_REVOCATION_LIST_ELEMENT;

typedef struct __tagDRM_REVOCATION_LIST
{
    DRM_BYTE  rgbSignature [__CB_DECL( PK_ENC_SIGNATURE_LEN) ];
    DRM_DWORD dwVersion;
    DRM_DWORD cb;
    DRM_DWORD idSerial;
    DRM_BYTE  rgbData [1]; /* An array of DRM_REVOCATION_LIST_ELEMENT structs which are variable length */
} DRM_REVOCATION_LIST;


static DRM_RESULT _CheckCertInRevocationList(
    DRM_CERT_TYPE        f_certificatetype,
    DRM_BYTE            *f_pbCertData,
    DRM_DWORD            f_cbCertData,
    DRM_REVOCATION_LIST *f_prevocationlist)
{
    DRM_BYTE  *pbElt    = f_prevocationlist->rgbData;
    DRM_DWORD  cbElt    = 0;
    DRM_DWORD  ibElt    = 0;
    DRM_RESULT dr       = DRM_SUCCESS;
    
    cbElt = f_prevocationlist->cb;
    FIX_ENDIAN_DWORD( cbElt );

    /* The revocation list is an array of DRM_REVOCATION_LIST_ELEMENT structs, so lets check them */
    while ( ibElt < cbElt )
    {
        DRM_CERT_TYPE  ecertType = 0;
        DRM_DWORD      cbCert    = 0;
        
        DRM_BYT_CopyBytes( &ecertType, 0, pbElt, ibElt, SIZEOF( DRM_DWORD ) );
        FIX_ENDIAN_DWORD( ecertType );
        ibElt += SIZEOF( DRM_CERT_TYPE );

        DRM_BYT_CopyBytes( &cbCert, 0, pbElt, ibElt, SIZEOF( DRM_DWORD ) );
        FIX_ENDIAN_DWORD( cbCert );
        ibElt += SIZEOF( DRM_DWORD );
        
        if ((ecertType == f_certificatetype) 
         && (cbCert    == f_cbCertData) 
         && (DRM_BYT_CompareBytes(f_pbCertData, 0, pbElt, ibElt, f_cbCertData) == 0))
        {
            ChkDR(DRM_E_CERTIFICATE_REVOKED);
        }

        ibElt += cbCert;
    }

ErrorExit:
    return dr;
}

/*****************************************************************************
** Function - DRM_RVK_PerformAppRevocationCheck
**
** Synopsis - calls app revocation operation 
**
** Arguments
** [f_pcontextLEVL] -- initialized liceval context
** [f_pcontextBBX]  -- initialized blackbox context
** [f_pcontextHDS]  -- initialized HDS context
*****************************************************************************/

#if DRM_SUPPORT_REVOCATION
DRM_RESULT DRM_API DRM_RVK_PerformAppRevocationCheck(
    IN DRM_LICEVAL_CONTEXT *f_pcontextLEVL,
    IN DRM_HDS_CONTEXT     *f_pcontextHDS)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_BYTE   rgbPassword [__CB_DECL(SHA_DIGEST_LEN)] = { 0x00 };
    DRM_CONST_STRING dstrAttributeData = EMPTY_DRM_STRING;
    DRM_DWORD cbBuffer = 0;

    ChkArg (f_pcontextLEVL != NULL
        &&  f_pcontextHDS  != NULL);

    dr = DRM_LIC_GetAttribute( &f_pcontextLEVL->dstrContentLicense, 
                               &g_dstrAppRevocation,
                               DRM_LICENSE_ATTRIB_REVOCATION, 
                               NULL,
                               &dstrAttributeData, 
                               0 );
                                
    if (dr == DRM_E_XMLNOTFOUND)
    {
        dr = DRM_SUCCESS; 
        goto ErrorExit;
    }

    ChkDR(dr);  /* other errors */
     
    ChkDR(_CreateRevocationStorePassword (f_pcontextLEVL->pcontextBBX, rgbPassword));

    /* Ensure latest app crl version is up to date */
    cbBuffer = f_pcontextLEVL->cbRevocationBuffer;
    ChkDR( DRM_RVK_GetCurrentAppRevocationList(f_pcontextLEVL->pcontextSSTRevocation,
                                            rgbPassword,
                                            f_pcontextLEVL->pbRevocationBuffer,
                                           &cbBuffer,
                                            f_pcontextHDS, 
                                           &f_pcontextLEVL->idCRLsCurrent.app ) );
    
    ChkDR(DRM_RVK_UpdateAppRevocationListW( &f_pcontextLEVL->pcontextBBX->CryptoContext, 
                                            f_pcontextLEVL->pcontextSSTRevocation,
                                            rgbPassword,
                                            f_pcontextLEVL->pbRevocationBuffer,
                                            f_pcontextLEVL->cbRevocationBuffer,
                                           &dstrAttributeData,
                                            f_pcontextHDS,
                                           &f_pcontextLEVL->appcert,
                                            f_pcontextLEVL->idCRLsCurrent.app,
                                           &f_pcontextLEVL->fUpdatedRevocationList));

ErrorExit:    
    if( dr == DRM_E_BUFFERTOOSMALL )
    {
        dr = DRM_E_REVOCATION_BUFFERTOOSMALL;
    }
    
    return dr;

}
#endif /* DRM_SUPPORT_REVOCATION */
/*****************************************************************************
** Function: DRM_RVK_GetCurrentAppRevocationList
**
** Synopsis: open the secure store and get the version number of the 
**           app revocation list currently stored
**
** Parameters:
**
** [f_pcontextSST]    -- secure store context
** [f_rgbPasswordSST] -- password used to retrieve secure store data
** [f_pbBuffer]       -- working buffer
** [f_pcbBuffer]      -- pointer to variable to receive app revocation list size
** [f_pcontextHDS]    -- initialized HDS context
** [f_pidCRLApp]      -- pointer to variable to receive app revocation list index
*****************************************************************************/
#if DRM_SUPPORT_REVOCATION
DRM_RESULT DRM_API DRM_RVK_GetCurrentAppRevocationList(
    IN      DRM_SECSTORE_CONTEXT *f_pcontextSST,
    IN      DRM_BYTE              f_rgbPasswordSST [__CB_DECL(SHA_DIGEST_LEN)],
    IN      DRM_BYTE             *f_pbBuffer,
    IN  OUT DRM_DWORD            *f_pcbBuffer,
    IN      DRM_HDS_CONTEXT      *f_pcontextHDS,
    OUT     DRM_DWORD            *f_pidCRLApp)
{
    DRM_RESULT dr    = DRM_SUCCESS;        

    ChkArg (f_pcontextSST    != NULL
        &&  f_rgbPasswordSST != NULL
        &&  f_pcontextHDS    != NULL
        &&  f_pidCRLApp      != NULL
        &&  f_pcbBuffer      != NULL);

    dr =  DRM_SST_GetData(f_pcontextSST,  
                         &g_lidAppRevocation, 
                          NULL,
                          f_rgbPasswordSST,
                          SECURE_STORE_REVOCATION_DATA,
                          f_pcontextHDS, 
                          f_pbBuffer, 
                          f_pcbBuffer);

    if (DRM_SUCCEEDED(dr))
    {
        ChkBOOL( f_pbBuffer != NULL,                        DRM_E_LOGICERR);
        ChkBOOL(*f_pcbBuffer > SIZEOF(DRM_REVOCATION_LIST), DRM_E_BUFFERTOOSMALL);
        
        *f_pidCRLApp = ((DRM_REVOCATION_LIST *) f_pbBuffer)->idSerial;

        FIX_ENDIAN_DWORD(*f_pidCRLApp);
    }
    else if (dr == DRM_E_BUFFERTOOSMALL)
    {
        ChkDR(DRM_E_BUFFERTOOSMALL);
    }
    else if (dr == DRM_E_FILENOTFOUND
          || dr == DRM_E_HDSSLOTNOTFOUND)
    {
        dr          = DRM_SUCCESS;
       *f_pidCRLApp = DRM_APP_REVOCATION_VERSION_NONE;
       *f_pcbBuffer = 0;
    }
    
ErrorExit:

    return dr;
}
#endif /* DRM_SUPPORT_REVOCATION */
/*****************************************************************************
** Function DRM_RVK_CheckAppCertForRevocation
**
** f_pbAppCRL -- DRM_REVOCATION_LIST passed in as byte buffer
** f_cbAppCRL -- size of the App CRL in bytes
** f_pappcert -- app certificate to check against what is in the store.
*****************************************************************************/
#if DRM_SUPPORT_REVOCATION
DRM_RESULT DRM_API DRM_RVK_CheckAppCertForRevocation(
    DRM_BYTE             *f_pbAppCRL,
    DRM_DWORD             f_cbAppCRL,
    APPCERT              *f_pappcert)
{
    DRM_RESULT dr = DRM_SUCCESS;        

    ChkArg(f_pbAppCRL    != NULL
        && f_pappcert    != NULL
        && f_cbAppCRL     > 0);
    
    ChkDR(_CheckCertInRevocationList(CERTTYPE_V1APP,
                       (DRM_BYTE *) &f_pappcert->appcd.pk.pk,
                             SIZEOF (f_pappcert->appcd.pk.pk),
            (DRM_REVOCATION_LIST *)  f_pbAppCRL));

ErrorExit:
    return dr;
}
#endif /* DRM_SUPPORT_REVOCATION */
/*****************************************************************************
** Function: DRM_RVK_UpdateAppRevocationListA
**
** Synopsis: Expanded version of DRM_RVK_PerformAppRevocationCheck.  Allows an 
**           app to query for app revocation without the lic eval context.
**
** Parameters:
**
** [pcontextCRYP]         -- Crypto buffer
** [f_pcontextSST]        -- secure store context
** [f_rgbPassword]        -- password that used to secure and validate the secure store data
** [f_pbBuffer]           -- working buffer
** [f_cbBuffer]           -- size of f_pbBuffer in bytes
** [f_pdstrRevocationList]-- Revocation list string that will be updated in the store 
** [f_pcontextHDS]        -- initialized HDS context
** [f_pappcert]           -- app certificate to check against what is in the store.
** [f_idCRLAppCurrent]    -- version number/index of the currently stored App CRL
** [f_pfUpdatedAppRevocationList] -- optional pointer to a bool variable to be set TRUE
**                           if the stored App CRL was updated.  PC-side callers
**                           should update cached global App CRL
 ****************************************************************************/

DRM_RESULT DRM_API DRM_RVK_UpdateAppRevocationListA(
        DRM_CRYPTO_CONTEXT   *f_pcontextCRY,
        DRM_SECSTORE_CONTEXT *f_pcontextSST,
        DRM_BYTE              f_rgbPassword [__CB_DECL(SHA_DIGEST_LEN)],
        DRM_BYTE             *f_pbBuffer,
        DRM_DWORD             f_cbBuffer,
        DRM_CHAR             *f_rgchBase,
  const DRM_SUBSTRING        *f_pdasstrRevocationList,
        DRM_HDS_CONTEXT      *f_pcontextHDS,
        APPCERT              *f_pappcert,
        DRM_DWORD             f_idCRLAppCurrent,
        DRM_BOOL             *f_pfUpdatedAppRevocationList)
{
    DRM_RESULT            dr              = DRM_SUCCESS;        

    ChkArg(f_pcontextCRY != NULL
        && f_pcontextSST != NULL
        && f_rgbPassword != NULL
        && f_pcontextHDS != NULL
        && f_pappcert    != NULL
        && f_cbBuffer     > 0
        && f_rgchBase     != NULL
        && f_pdasstrRevocationList != NULL );
    

    if (f_pfUpdatedAppRevocationList != NULL)
    {
       *f_pfUpdatedAppRevocationList = FALSE;
    }


    /* If they don't pass us a buffer, it's a signal to just decode in place */
    if( f_pbBuffer == NULL )
    {

        /* pbOffset will point within the base64 revocation list we were passed,
           such that after base64 decoding the end of the decoded data will co-incide with
           the end of the buffer we were given, minus one byte.

           This is because we are giving the B64 decoder overlapping memory for source and desitination,
           and we can't use the DECODE_IN_PLACE flag because this is ANSI where the offset may not coincide
           with a word boundary as required on 16-bit platforms.
         */

        f_pbBuffer = (DRM_BYTE*)f_rgchBase
                            + __CB_DECL( f_pdasstrRevocationList->m_ich + f_pdasstrRevocationList->m_cch - CB_BASE64_DECODE( f_pdasstrRevocationList->m_cch ) - 1 )
                            + ( ( f_pdasstrRevocationList->m_ich + f_pdasstrRevocationList->m_cch - CB_BASE64_DECODE( f_pdasstrRevocationList->m_cch ) ) % CB_NATIVE_BYTE );
 		
 		f_cbBuffer = CB_BASE64_DECODE( f_pdasstrRevocationList->m_cch ) + 1 - ( ( f_pdasstrRevocationList->m_ich + f_pdasstrRevocationList->m_cch - CB_BASE64_DECODE( f_pdasstrRevocationList->m_cch ) ) % CB_NATIVE_BYTE );

        ChkDR( DRM_B64_DecodeA( f_rgchBase,
                    f_pdasstrRevocationList,
                   &f_cbBuffer, 
                    f_pbBuffer, 
                    0 ) );
    }
    else
    {
        ChkDR( DRM_B64_DecodeA( f_rgchBase,
                    f_pdasstrRevocationList,
                   &f_cbBuffer, 
                    f_pbBuffer, 
                    0 ) );
    }

    ChkDR( _UpdateAppRevocationList( f_pcontextCRY,
                f_pcontextSST,
                f_rgbPassword,
                f_pbBuffer,
                f_cbBuffer,
                f_pcontextHDS,
                f_pappcert,
                f_idCRLAppCurrent,
                f_pfUpdatedAppRevocationList ) );


ErrorExit:
    return dr;
}

DRM_RESULT DRM_API DRM_RVK_UpdateAppRevocationListW(
    DRM_CRYPTO_CONTEXT   *f_pcontextCRY,
    DRM_SECSTORE_CONTEXT *f_pcontextSST,
    DRM_BYTE              f_rgbPassword [__CB_DECL(SHA_DIGEST_LEN)],
    DRM_BYTE             *f_pbBuffer,
    DRM_DWORD             f_cbBuffer,
    DRM_CONST_STRING     *f_pdstrRevocationList,
    DRM_HDS_CONTEXT      *f_pcontextHDS,
    APPCERT              *f_pappcert,
    DRM_DWORD             f_idCRLAppCurrent,
    DRM_BOOL             *f_pfUpdatedAppRevocationList)
{
    DRM_RESULT            dr              = DRM_SUCCESS;        

    ChkArg(f_pcontextCRY             != NULL
        && f_pcontextSST              != NULL
        && f_rgbPassword              != NULL
        && f_pcontextHDS              != NULL
        && f_pappcert                 != NULL
        && f_cbBuffer                  > 0
        && f_pdstrRevocationList     != NULL );
    

    if (f_pfUpdatedAppRevocationList != NULL)
    {
       *f_pfUpdatedAppRevocationList = FALSE;
    }

    if( f_pbBuffer == NULL )
    {
        /* If they don't pass us a buffer, it's a signal to just decode in place */

        ChkDR( DRM_B64_DecodeW(f_pdstrRevocationList, 
                       &f_cbBuffer, 
                        NULL, 
                        DRM_BASE64_DECODE_IN_PLACE ) );
        
        f_pbBuffer = (DRM_BYTE*)f_pdstrRevocationList->pwszString;
    }
    else
    {
        ChkDR( DRM_B64_DecodeW(f_pdstrRevocationList, 
                       &f_cbBuffer, 
                        f_pbBuffer, 
                        0 ) );
    }
    
    ChkDR( _UpdateAppRevocationList( f_pcontextCRY,
                f_pcontextSST,
                f_rgbPassword,
                f_pbBuffer,
                f_cbBuffer,
                f_pcontextHDS,
                f_pappcert,
                f_idCRLAppCurrent,
                f_pfUpdatedAppRevocationList ) );


ErrorExit:
    return dr;
}

DRM_RESULT _UpdateAppRevocationList(
    DRM_CRYPTO_CONTEXT   *f_pcontextCRY,
    DRM_SECSTORE_CONTEXT *f_pcontextSST,
    DRM_BYTE              f_rgbPassword [__CB_DECL(SHA_DIGEST_LEN)],
    DRM_BYTE             *f_pbRevocationList,
    DRM_DWORD             f_cbRevocationList,
    DRM_HDS_CONTEXT      *f_pcontextHDS,
    APPCERT              *f_pappcert,
    DRM_DWORD             f_idCRLAppCurrent,
    DRM_BOOL             *f_pfUpdatedAppRevocationList)
{
    DRM_RESULT            dr              = DRM_SUCCESS;        
    DRM_DWORD             idCRLAppNew     = 0;
    DRM_REVOCATION_LIST  *prevocationlist = NULL;
    DRM_DWORD             cbSigned         = 0;
    DRM_DWORD             dwVersion        = 0;


    ChkArg(f_pcontextCRY != NULL
        && f_pcontextSST != NULL
        && f_rgbPassword != NULL
        && f_pcontextHDS != NULL
        && f_pappcert    != NULL
        && f_pbRevocationList     != NULL
        && f_cbRevocationList > 0 );


    if (f_pfUpdatedAppRevocationList != NULL)
    {
       *f_pfUpdatedAppRevocationList = FALSE;
    }

    prevocationlist = (DRM_REVOCATION_LIST *)  f_pbRevocationList;
    dwVersion           = prevocationlist->dwVersion;
    
    FIX_ENDIAN_DWORD( dwVersion ); 
    
    if (dwVersion != CURRENT_REVOCATION_VERSION)
    {
        ChkDR(DRM_E_NOTIMPL);
    }

    idCRLAppNew = prevocationlist->idSerial;
    FIX_ENDIAN_DWORD( idCRLAppNew );

    if (f_idCRLAppCurrent == DRM_APP_REVOCATION_VERSION_NONE
    ||  f_idCRLAppCurrent  < idCRLAppNew)
    {
        /* Verify & store the new list. */

        cbSigned = prevocationlist->cb;
        FIX_ENDIAN_DWORD(cbSigned);

        cbSigned += ( 3 * SIZEOF( DRM_DWORD ) );

        if (DRM_PK_Verify(f_pcontextCRY, 
                         &g_pubkeyRevocation, 
            (DRM_BYTE *) &prevocationlist->dwVersion,
                          cbSigned,
                          prevocationlist->rgbSignature))
        {
            /* The new list is verified.  Store it. */
            
            ChkDR(DRM_SST_SetData(f_pcontextSST, 
                                 &g_lidAppRevocation, 
                                  NULL,
                                  f_rgbPassword,
                                  SECURE_STORE_REVOCATION_DATA,
                                  f_pcontextHDS, 
                                  f_pbRevocationList, 
                                  f_cbRevocationList));

            if (f_pfUpdatedAppRevocationList != NULL)
            {
                *f_pfUpdatedAppRevocationList = TRUE;
            }                                  
        }

        ChkDR(_CheckCertInRevocationList(CERTTYPE_V1APP,
                        (DRM_BYTE *) &f_pappcert->appcd.pk.pk,
                                SIZEOF (f_pappcert->appcd.pk.pk),
                                        prevocationlist));
    }

ErrorExit:
    return dr;

}
#endif


#if DRM_SUPPORT_REVOCATION
/******************************************************************************
** 
** Function :   DRM_RVK_UpdateRevocationVersionsCache
** 
** Synopsis :   Loads the revinfo and CRL versions from the secure store
**              and caches them in the DRM_MANAGER_CONTEXT for use when
**              evaluting licenses with revocation requirements
**              
** 
******************************************************************************/
DRM_RESULT DRM_API DRM_RVK_UpdateRevocationVersionsCache( 
 IN OUT DRM_MANAGER_CONTEXT_INTERNAL  *f_pContext,
    OUT DRM_BOOL                     *f_pfUpdated)
{
    DRM_RESULT       dr             = DRM_SUCCESS;
    DRM_DWORD        cbBuffer       = 0;
    DRM_RLVI         RLVI;
    DRM_DWORD        dwVersion      = 0;
    DRM_BYTE         rgbPassword [__CB_DECL(SHA_DIGEST_LEN)];

    ChkArg( f_pContext                           != NULL
        &&  f_pContext->fSecStoreGlobalContextOpen );

    MEMSET( &RLVI, 0, SIZEOF( DRM_RLVI ) );
    if( f_pfUpdated != NULL )
    {
        *f_pfUpdated = FALSE;
    }

    cbBuffer = f_pContext->oLicEvalContext.cbRevocationBuffer;
    /* Update the revocation info version */
    ChkDR( DRM_RVK_GetCurrentRevocationInfo( &f_pContext->oSecStoreGlobalContext,
                                           &f_pContext->oBlackBoxContext,
                                           &f_pContext->oHdsContext,
                                            f_pContext->oLicEvalContext.pbRevocationBuffer,
                                           &cbBuffer,
                                           &RLVI ) );


    if( f_pContext->idCRLsCurrent.riv != RLVI.head.dwRIV )
    {
        if( f_pfUpdated != NULL )
        {
            *f_pfUpdated = TRUE;
        }
        f_pContext->idCRLsCurrent.riv = RLVI.head.dwRIV;
    }


#if DRM_SUPPORT_DEVICE_REVOCATION || DRM_SUPPORT_WMDRMNET
    ChkDR(_CreateRevocationStorePassword (&f_pContext->oBlackBoxContext, rgbPassword));
#endif


#if DRM_SUPPORT_DEVICE_REVOCATION
    cbBuffer = f_pContext->oLicEvalContext.cbRevocationBuffer;
    /* Update the device CRL version */
    ChkDR( DRM_RVK_GetDeviceRevocationList(&f_pContext->oBlackBoxContext.CryptoContext,
                                        &f_pContext->oSecStoreGlobalContext,
                                        &f_pContext->oHdsContext,
                                         rgbPassword,
                                         f_pContext->oLicEvalContext.pbRevocationBuffer,
                                        &cbBuffer,
                                        &dwVersion) );


    if( f_pContext->idCRLsCurrent.device != dwVersion )
    {
        if( f_pfUpdated != NULL )
        {
            *f_pfUpdated = TRUE;
        }
        f_pContext->idCRLsCurrent.device = dwVersion;
    }
#endif

#if DRM_SUPPORT_WMDRMNET
    cbBuffer = f_pContext->oLicEvalContext.cbRevocationBuffer;
    /* Update the WMDRMNET CRL version */
    ChkDR( DRM_RVK_GetWMDRMNETList(&f_pContext->oBlackBoxContext.CryptoContext,
                                        &f_pContext->oSecStoreGlobalContext,
                                        &f_pContext->oHdsContext,
                                         rgbPassword,
                                         f_pContext->oLicEvalContext.pbRevocationBuffer,
                                        &cbBuffer,
                                        &dwVersion) );


    if( f_pContext->idCRLsCurrent.wmdrmnet != dwVersion )
    {
        if( f_pfUpdated != NULL )
        {
            *f_pfUpdated = TRUE;
        }
        f_pContext->idCRLsCurrent.wmdrmnet = dwVersion;
    }
#endif

ErrorExit:

    if( dr == DRM_E_BUFFERTOOSMALL )
    {
        dr = DRM_E_REVOCATION_BUFFERTOOSMALL;
    }

    return dr;  
}
#endif /* DRM_SUPPORT_REVOCATION */



#if DRM_SUPPORT_DEVICE_REVOCATION || DRM_SUPPORT_WMDRMNET

/*****************************************************************************
** Function: IsWrappedCRL
**
** Synopsis: Checks if the CRL is wrapped with <DATA> tag or not. Used to find
**           out if the WMDRMNET CRL is with wrappers.
**
** [f_pdstrRevocationList]  -- Revocation list
*****************************************************************************/
static DRM_BOOL IsWrappedCRL( IN DRM_CONST_STRING *f_pdstrRevocationList )
{
    DRM_RESULT       dr       = DRM_SUCCESS;
    DRM_CONST_STRING dstrData = EMPTY_DRM_STRING;    

    /*Check if the revocation list contains <DATA> tag */
    dr = DRM_XML_GetNode( f_pdstrRevocationList,
                          &g_dstrTagData,
                          NULL,
                          NULL,
                          0,
                          NULL,
                          &dstrData );
    if ( dr == DRM_E_XMLNOTFOUND )
    {
        return FALSE;
    }
    else
    {
        return TRUE;
    }
}

/*****************************************************************************
** Function: _ExtractRevocationListA
**
** Synopsis: calls app revocation operation 
**
** [f_pdstrLicenseXML]      -- entire license response XML 
** [f_pbBuffer]             -- buffer
** [f_pcbBuffer]            -- pointer to buffer size
** [f_pdstrRevocationType]  -- the revocation attribute type
** [f_pdstrAttributeData]   -- on success contains the substring for the given
**                             revocation type
*****************************************************************************/
static DRM_RESULT _ExtractRevocationList(
    IN       DRM_ANSI_CONST_STRING *f_pdastrLicenseResponse,
    IN const DRM_ANSI_CONST_STRING *f_pdastrRevocationType, 
    OUT      DRM_SUBSTRING         *f_pdasstrAttributeData)
{
    DRM_RESULT    dr                      = DRM_SUCCESS;
    DRM_SUBSTRING dasstrLicenseResponse = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrList             = EMPTY_DRM_SUBSTRING;


    ChkArg(f_pdastrLicenseResponse     != NULL
        && f_pdastrRevocationType       != NULL 
        && f_pdasstrAttributeData       != NULL);

    /* get the uppermost license list */
    
    dasstrLicenseResponse.m_ich = 0;
    dasstrLicenseResponse.m_cch = f_pdastrLicenseResponse->cchString;
    
    ChkDR( DRM_XML_GetNodeA( f_pdastrLicenseResponse->pszString,
                             &dasstrLicenseResponse,
                             &g_adstrLicenseRespTag,    
                             NULL, 
                             NULL, 
                             0, 
                             NULL, 
                             &dasstrList ) );

    /* get the node for this revocation type */
                          
    ChkDR( DRM_XML_GetNodeA( f_pdastrLicenseResponse->pszString,
                             &dasstrList,       
                             &g_dastrTagRevocation, 
                             &g_dastrAttributeType, 
                             f_pdastrRevocationType, 
                             0, 
                             NULL,
                             f_pdasstrAttributeData ) );
    
ErrorExit:
    return dr;    
}    

/******************************************************************************
** 
** Function :   _VerifyRevocationList
** 
** Synopsis :   Verify signature on the revocation list
** 
** Arguments :  f_pcontextCRYP  - Crypto context
**              f_pdstrList     - Revocation list
**              f_ppubkey       - Public key for verifying signature on 
**                                revocation list
**              f_pidSerial     - Serial number on revocation list
** 
** Returns :    
** 
** Notes :      
** 
******************************************************************************/
static DRM_RESULT _VerifyRevocationList(
    IN          DRM_CRYPTO_CONTEXT *f_pcontextCRYP, 
    IN          DRM_CONST_STRING   *f_pdstrList,
    IN  const   PUBKEY             *f_ppubkey,
        OUT     DRM_DWORD          *f_pidSerial)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_CONST_STRING dstrListData      = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrListSignature = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrNodeHashAlg   = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrNodeSignAlg   = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrDataHashAlg   = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrDataSignature = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrDataSignAlg   = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrDataIndex     = EMPTY_DRM_STRING;

    /* verify the signature attributes and get the value */

    ChkDR(DRM_XML_GetNode(f_pdstrList,        &g_dstrTagSignature, NULL, NULL, 0,  NULL,            &dstrListSignature));
    ChkDR(DRM_XML_GetNode(&dstrListSignature, &g_dstrTagHashAlg,   NULL, NULL, 0, &dstrNodeHashAlg,  NULL));
    ChkDR(DRM_XML_GetNode(&dstrListSignature, &g_dstrTagSignAlg,   NULL, NULL, 0, &dstrNodeSignAlg,  NULL));
    ChkDR(DRM_XML_GetNode(&dstrListSignature, &g_dstrTagValue,     NULL, NULL, 0,  NULL,            &dstrDataSignature));

    ChkDR(DRM_XML_GetNodeAttribute(&dstrNodeHashAlg, &g_dstrAttributeType, &dstrDataHashAlg));
    ChkDR(DRM_XML_GetNodeAttribute(&dstrNodeSignAlg, &g_dstrAttributeType, &dstrDataSignAlg));

    ChkFAIL(DRM_UTL_DSTRStringsEqual(&dstrDataHashAlg, &g_dstrSHA));
    ChkFAIL(DRM_UTL_DSTRStringsEqual(&dstrDataSignAlg, &g_dstrMSDRM)); 

    /* verify the signature against the <DATA> section */

    ChkDR( DRM_XML_GetAndVerifyNode( f_pdstrList, 
                                     &g_dstrTagData,
                                     NULL,
                                     NULL,
                                     0,
                                     f_pcontextCRYP,
                                     f_ppubkey,
                                     TRUE,
                                     &dstrDataSignature,
                                     NULL,
                                     &dstrListData ) );

    /* verification successful; get the index (version) of this list */

    ChkDR( DRM_XML_GetNode( &dstrListData,            /*    <INDEX> */
                            &g_dstrTagIndex,
                            NULL,
                            NULL,
                            0,
                            NULL,
                            &dstrDataIndex ) );

    ChkDR( wcsntol( dstrDataIndex.pwszString,
                    dstrDataIndex.cchString,
                    (DRM_LONG *) f_pidSerial ) );
#if DRM_SUPPORT_WMDRMNET
    if( MEMCMP( f_ppubkey, &g_pubkeyCardeaRevocation, sizeof( *f_ppubkey ) ) == 0 )
    {
        DRM_CONST_STRING dstrTemplate = EMPTY_DRM_STRING;

        ChkDR( DRM_XML_GetSubNode(  f_pdstrList, 
                                   &g_dstrTemplate, 
                                    NULL, 
                                    NULL, 
                                    0, 
                                    NULL,
                                   &dstrTemplate,
                                    1 ) );

        /*
        ** If this is a WMDRMNET revocation list use the OEM function to 
        ** validate the internal representation of the list
        */

        ChkDR(_VerifyWMDRMNETRevocationList(&dstrTemplate,
                                          f_pidSerial));

    }
#endif
                          
ErrorExit:
    return dr;
} 

/******************************************************************************
** 
** Function :   _GetSSTRevocationList
** 
** Synopsis :   Get and verify revocation list from Secure store
** 
** Arguments :  
** 
** Returns :    
** 
** Notes :      
** 
******************************************************************************/
static DRM_RESULT _GetSSTRevocationList(
    IN  const   DRM_LID              *f_plid,
    IN          DRM_CRYPTO_CONTEXT   *f_pcontextCRYP, 
    IN          DRM_SECSTORE_CONTEXT *f_pcontextSST,
    IN          DRM_HDS_CONTEXT      *f_pcontextHDS,
    IN          DRM_BYTE              f_rgbPassword [__CB_DECL(SHA_DIGEST_LEN)],
    IN  const   PUBKEY               *f_ppubkey,
        OUT     DRM_BYTE             *f_pbBuffer,
    IN  OUT     DRM_DWORD            *f_pcbBuffer,
        OUT     DRM_DWORD            *f_pidSerial)
{
    DRM_RESULT dr            = DRM_SUCCESS;        
    DRM_BYTE  *pbAligned     = NULL;
    DRM_DWORD  cbAligned     = 0;                    
    DRM_CONST_STRING dstrXML = EMPTY_DRM_STRING;
        
    ChkArg(f_plid         != NULL  
       &&  f_pcontextCRYP != NULL
       &&  f_pcontextSST  != NULL
       &&  f_rgbPassword  != NULL
       &&  f_ppubkey      != NULL
       &&  f_pcontextHDS  != NULL
       &&  f_pcbBuffer    != NULL);

    *f_pidSerial = DRM_NO_PREVIOUS_CRL;
    
    /* get the current SST image */

    if (f_pbBuffer != NULL)
    {
        ChkDR( DRM_UTL_EnsureDataAlignment( f_pbBuffer, 
                                            *f_pcbBuffer, 
                                            &pbAligned, 
                                            &cbAligned, 
                                            SIZEOF (DRM_WCHAR),
                                            NULL ) );
    }
    
    dr = DRM_SST_GetData( f_pcontextSST, 
                          f_plid, 
                          NULL,
                          f_rgbPassword,
                          SECURE_STORE_GLOBAL_DATA,
                          f_pcontextHDS, 
                          pbAligned, 
                          &cbAligned );    

    *f_pcbBuffer = cbAligned;    

    /* includes the DRM_E_BUFFERTOOSMALL case */

    ChkDR(dr);
    
    DSTR_FROM_PB( &dstrXML, pbAligned, cbAligned );

    /* verify the signature and extract the index of this exclusion list */
    if ( IsWrappedCRL( &dstrXML ) )
    {
        ChkDR( _VerifyRevocationList( f_pcontextCRYP, 
                                      &dstrXML, 
                                      f_ppubkey, 
                                      f_pidSerial ) );    
    }
    else
    {
#if DRM_SUPPORT_WMDRMNET
        ChkDR( _VerifyBinaryWMDRMNETSignature( pbAligned,
                                              cbAligned,
                                              f_pidSerial ) );
#else
        ChkDR( DRM_E_NOTIMPL );
#endif
    }

                                           
ErrorExit:

    return dr;
}

#endif /* #if DRM_SUPPORT_DEVICE_REVOCATION || DRM_SUPPORT_WMDRMNET */

#if DRM_SUPPORT_DEVICE_REVOCATION

/******************************************************************************
** 
** Function :   _UpdateRevocationList
** 
** Synopsis :   updates the revocation list in the secure store
** 
** Arguments :  
** [f_pcontextCrypto]      -- 
** [f_pcontextSST]         -- 
** [f_rgbPasswordSST]      -- created with the BBX hash
** [f_pbRevocationList]    -- input buffer for exclusion list
** [f_cbRevocationList]    -- buffer size
** [f_pdstrRevocationList] -- the new DCRL
** [f_pID]                 -- DRM_ID used to store this list in secure store
** [f_pcontextHDS]         -- initialized HDS context

** Returns :    
** 
** Notes :      
** 
******************************************************************************/
static DRM_RESULT _UpdateRevocationList(
    IN          DRM_CRYPTO_CONTEXT   *f_pcontextCrypto,
    IN          DRM_SECSTORE_CONTEXT *f_pcontextSST,
    IN          DRM_BYTE              f_rgbPassword[__CB_DECL(SHA_DIGEST_LEN)],
    IN          DRM_BYTE             *f_pbRevocationList,
    IN          DRM_DWORD             f_cbRevocationList,
    IN          DRM_BYTE             *f_pbRevocationBuffer,
    IN          DRM_DWORD             f_cbRevocationBuffer,
    IN  const   DRM_ID               *f_pID,
    IN  const   PUBKEY               *f_ppubkey,
    IN          DRM_HDS_CONTEXT      *f_pcontextHDS )
{
    DRM_RESULT dr          = DRM_SUCCESS;        
    DRM_DWORD  idSerialNew = 0,
               idSerialOld = 0,
               cbReturned  = 0;
    DRM_STRING dstrRevocationList = EMPTY_DRM_STRING;
    
    ChkArg(   f_pcontextCrypto  != NULL
           && f_pcontextSST      != NULL
           && f_rgbPassword      != NULL
           && f_pID               != NULL
           && f_pcontextHDS      != NULL
           && f_pbRevocationList != NULL
           && f_pbRevocationBuffer != NULL
           && f_cbRevocationBuffer > 0
           && f_cbRevocationBuffer > 0 );  
    

    DSTR_FROM_PB( &dstrRevocationList, f_pbRevocationList, f_cbRevocationList );

    idSerialNew = DRM_NO_PREVIOUS_CRL;
    idSerialOld = DRM_NO_PREVIOUS_CRL;


    ChkDR( _VerifyRevocationList( f_pcontextCrypto, 
                  (DRM_CONST_STRING*)&dstrRevocationList, 
                                      f_ppubkey,
                                     &idSerialNew ) );

    cbReturned = f_cbRevocationBuffer;    
    /* get and verify the current SST image
     */
    dr = _GetSSTRevocationList( f_pID, 
                                f_pcontextCrypto, 
                                f_pcontextSST,
                                f_pcontextHDS,
                                f_rgbPassword,
                                f_ppubkey,
                                f_pbRevocationBuffer,
                               &cbReturned,
                               &idSerialOld );


    /* This can be the first Revocation list */

    if ( dr == DRM_E_FILENOTFOUND 
        || ( ( idSerialOld == DRM_NO_PREVIOUS_CRL ) && DRM_SUCCEEDED( dr ) )
        || ( ( idSerialOld < idSerialNew ) && DRM_SUCCEEDED( dr ) ) )
    {
        cbReturned = f_cbRevocationBuffer;
        
        /* Store the list as a wrapped CRL */
        ChkDR(DRM_SST_SetData( f_pcontextSST, 
                               f_pID, 
                               NULL,
                               f_rgbPassword,
                               SECURE_STORE_GLOBAL_DATA,
                               f_pcontextHDS, 
                               f_pbRevocationList, 
                               f_cbRevocationList ) );
    }
    else
    {
        ChkDR( dr );    
    }

ErrorExit:
    
    return dr;
} 

/******************************************************************************
** 
** Function :   DRM_RVK_UpdateRevocationList
** 
** Synopsis :   updates the revocation list in the secure store
** 
** Arguments :  
** [f_pcontextCrypto]      -- 
** [f_pcontextSST]         -- 
** [f_rgbPasswordSST]      -- created with the BBX hash
** [f_pbRevocationList]    -- input buffer for exclusion list
** [f_cbRevocationList]    -- buffer size
** [f_pdstrRevocationList] -- the new DCRL
** [f_pID]                 -- DRM_ID used to store this list in secure store
** [f_pcontextHDS]         -- initialized HDS context

** Returns :    
** 
** Notes :      
** 
******************************************************************************/
DRM_RESULT DRM_RVK_UpdateRevocationList(
    IN          DRM_CRYPTO_CONTEXT   *f_pcontextCrypto,
    IN          DRM_SECSTORE_CONTEXT *f_pcontextSST,
    IN          DRM_BYTE              f_rgbPassword[__CB_DECL(SHA_DIGEST_LEN)],
    IN          DRM_CHAR             *f_rgchBase,
    IN  const   DRM_SUBSTRING        *f_pdasstrRevocationList,
    IN          DRM_BYTE             *f_pbRevocationBuffer,
    IN          DRM_DWORD             f_cbRevocationBuffer,
    IN  const   DRM_ID               *f_pID,
    IN  const   PUBKEY               *f_ppubkey,
    IN          DRM_HDS_CONTEXT      *f_pcontextHDS )
{
    DRM_RESULT       dr             = DRM_SUCCESS;
    DRM_DWORD        cbDecoded      = 0;
    DRM_BYTE        *pbDecoded      = NULL;

    ChkArg(   f_pcontextCrypto  != NULL
           && f_pcontextSST      != NULL
           && f_rgbPassword      != NULL
           && f_pID               != NULL
           && f_pcontextHDS      != NULL
           && f_rgchBase            != NULL
           && f_pbRevocationBuffer != NULL
           && f_cbRevocationBuffer > 0
           && f_pdasstrRevocationList != NULL
           && f_pdasstrRevocationList->m_cch > 0 );  

    /* pbOffset will point within the base64 revocation list we were passed,
       such that after base64 decoding the end of the decoded data will co-incide with
       the end of the buffer we were given, minus one byte.

       This is because we are giving the B64 decoder overlapping memory for source and desitination,
       and we can't use the DECODE_IN_PLACE flag because this is ANSI where the offset may not coincide
       with a word boundary as required on 16-bit platforms.
     */
    pbDecoded = (DRM_BYTE*)f_rgchBase
                        + __CB_DECL( f_pdasstrRevocationList->m_ich + f_pdasstrRevocationList->m_cch - CB_BASE64_DECODE( f_pdasstrRevocationList->m_cch ) - 1 )
                        + ( ( f_pdasstrRevocationList->m_ich + f_pdasstrRevocationList->m_cch - CB_BASE64_DECODE( f_pdasstrRevocationList->m_cch ) ) % CB_NATIVE_BYTE );

	cbDecoded = CB_BASE64_DECODE( f_pdasstrRevocationList->m_cch ) + 1 - ( ( f_pdasstrRevocationList->m_ich + f_pdasstrRevocationList->m_cch - CB_BASE64_DECODE( f_pdasstrRevocationList->m_cch ) ) % CB_NATIVE_BYTE );

    /* decode the XML for the new exclusion list */
    ChkDR( DRM_B64_DecodeA( f_rgchBase,
                            f_pdasstrRevocationList, 
                            &cbDecoded, 
                            pbDecoded, 
                            0 ) );

    ChkDR( _UpdateRevocationList( f_pcontextCrypto, 
                                    f_pcontextSST, 
                                    f_rgbPassword, 
                                    pbDecoded, 
                                    cbDecoded, 
                                    f_pbRevocationBuffer,
                                    f_cbRevocationBuffer, 
                                    f_pID, 
                                    f_ppubkey, 
                                    f_pcontextHDS ) );


ErrorExit:
    if( ( f_rgchBase != NULL ) 
        && ( f_pdasstrRevocationList != NULL )
        && ( f_pdasstrRevocationList->m_cch != 0 ) )
    {
        /* Since we decoded in-place, we need to clean up otherwise the XML parsing may
         * not work properly later on. So we have to overwrite our binary with spaces.
         *
         * NOTE: This means that we CANNOT process the same revocation list in the same
         * license response buffer twice since we're overwriting the buffer we were given
         */
        DRM_BYT_SetBytes( f_rgchBase, f_pdasstrRevocationList->m_ich, f_pdasstrRevocationList->m_cch, ' ');
    }
    
    return dr;
}



/*****************************************************************************
** Function: _UpdateDeviceRevocationList
**
** Synopsis: updates the device exclusion list in the secure store
**
** Arguments:
** [f_pcontextCRYP]        -- just a buffer
** [f_pcontextSST]         -- just a buffer
** [f_rgbPasswordSST]      -- created with the BBX hash
** [f_pbBuffer]            -- input buffer for exclusion list
** [f_cbBuffer]            -- buffer size
** [f_pdstrDeviceRevocationList] -- the new DCRL
** [f_pcontextHDS]         -- initialized* HDS context
*****************************************************************************/
static DRM_RESULT _UpdateDeviceRevocationList(
    IN DRM_CRYPTO_CONTEXT   *f_pcontextCRYP,
    IN DRM_SECSTORE_CONTEXT *f_pcontextSST,
    IN DRM_BYTE              f_rgbPassword [__CB_DECL(SHA_DIGEST_LEN)],
    IN DRM_CHAR             *f_rgchBase,
    IN DRM_SUBSTRING        *f_pdasstrRevocationList,
    IN DRM_BYTE             *f_pbRevocationBuffer,
    IN DRM_DWORD             f_cbRevocationBuffer,
    IN DRM_HDS_CONTEXT      *f_pcontextHDS )
{
    return DRM_RVK_UpdateRevocationList( f_pcontextCRYP,
                                  f_pcontextSST,
                                  f_rgbPassword,
                                  f_rgchBase,
                                  f_pdasstrRevocationList,
                                  f_pbRevocationBuffer,
                                  f_cbRevocationBuffer,
                                  &g_lidDeviceRevocation,
                                  &g_pubkeyDeviceRevocation,
                                  f_pcontextHDS );
} 


/*****************************************************************************
** Function: DRM_RVK_GetDeviceRevocationList
**
** Synopsis: gets the current device exclusion list from the secure store
**
** Arguments:
** [f_pcontextCRYP]      -- just a buffer
** [f_pcontextSST]       -- just a buffer
** [f_pcontextHDS]       -- initialized* HDS context
** [f_rgbPasswordSST]    -- created with the BBX hash
** [f_pbBuffer]          -- output buffer for exclusion list; NULL to request 
**                          required size
** [f_pcbBuffer]         -- DRM_DWORD to hold max buffer size on in, bytes actually 
**                          used on out
** [f_pidSerial]         -- receives numerical representation of <INDEX> tag
*****************************************************************************/
#if DRM_SUPPORT_REVOCATION
DRM_RESULT DRM_API DRM_RVK_GetDeviceRevocationList(
    IN     DRM_CRYPTO_CONTEXT   *f_pcontextCRYP, 
    IN     DRM_SECSTORE_CONTEXT *f_pcontextSST,
    IN     DRM_HDS_CONTEXT      *f_pcontextHDS,
    IN     DRM_BYTE              f_rgbPassword [__CB_DECL(SHA_DIGEST_LEN)],
    OUT    DRM_BYTE             *f_pbRevocationData,
    IN OUT DRM_DWORD            *f_pcbRevocationData,
    OUT    DRM_DWORD            *f_pidSerial)
{
    DRM_RESULT  dr = DRM_SUCCESS;
    ChkArg(   f_pcontextCRYP        != NULL
           && f_pcontextSST         != NULL
           && f_pcontextHDS         != NULL
           && f_rgbPassword         != NULL
           && f_pcbRevocationData   != NULL
           && f_pidSerial           != NULL );
    
    dr = _GetSSTRevocationList( &g_lidDeviceRevocation, 
                                  f_pcontextCRYP, 
                                  f_pcontextSST,
                                  f_pcontextHDS,
                                  f_rgbPassword,
                                  &g_pubkeyDeviceRevocation,
                                  f_pbRevocationData,
                                  f_pcbRevocationData,
                                  f_pidSerial );
    if ( dr == DRM_E_FILENOTFOUND
      || dr == DRM_E_HDSSLOTNOTFOUND )
    {
        dr                   = DRM_SUCCESS;
        *f_pidSerial         = 0;
        *f_pcbRevocationData = 0;
        goto ErrorExit;
    }
    ChkDR(dr);
    
ErrorExit:
    return dr;
}
#endif /* DRM_SUPPORT_REVOCATION */

/*****************************************************************************
** Function: DRM_RVK_CheckDeviceRevocation
**
** Synopsis: compare a devcert to an exclusion entry and seek a match result
**
** Arguments:
** [f_pdevcert]          -- a DEVICE_CERT struct pointing to the devcert XML
** [f_pdstrXMLExclusion] -- a DSTR pointing to an exclusion list entry with the 
** [f_pfExcluded]        -- pointer to a boolean variable to hold the match
**                          result
** Returns: - DRM_SUCCESS regardless of match 
**          - error code on XML or numerical translation failure
**
** Notes:   TODO: Just take this function out. Leaving it here for now as
**          the overall checkin is becoming too unwieldy
**
*****************************************************************************/
DRM_RESULT DRM_API DRM_RVK_CheckDeviceRevocation(
    IN     DRM_CONST_STRING *f_pdstrdevcert,
    IN     DRM_CONST_STRING *f_pdstrXMLExclusion, 
       OUT DRM_BOOL         *f_pfExcluded)
{
    DRM_RESULT dr       = DRM_SUCCESS;

    ChkArg(f_pfExcluded != NULL);
    ChkDRMString(f_pdstrXMLExclusion);
    ChkDRMString(f_pdstrdevcert);

    *f_pfExcluded = FALSE;

    /* get the devcert XML with the <DATA> tag at the root */
    ChkDR(DRM_XMU_MatchNodeFromTemplate(f_pdstrdevcert,
                                        f_pdstrXMLExclusion,
                                        TRUE,
                                        f_pfExcluded));
    
ErrorExit:          
    return dr;
} 

#endif /* DRM_SUPPORT_DEVICE_REVOCATION */

#if DRM_SUPPORT_WMDRMNET 

/******************************************************************************
** 
** Function :   _VerifyWMDRMNETRevocationList
** 
** Synopsis :   Verify signature on the WMDRMNET CRL and return the version
**              of the CRL.
** 
** Arguments :  f_pdstrList     - WMDRMNET Revocation list
**              f_pdwVersion    - Version or Serial number on revocation list
** 
** Returns :    
** 
** Notes :      If the verification fails, the buffer will be filled with
**              spaces to prevent the XML parsing from being confused later
**
******************************************************************************/
static DRM_RESULT _VerifyWMDRMNETRevocationList(
    IN  DRM_CONST_STRING   *f_pdstrList,
    OUT DRM_DWORD          *f_pdwVersion )
{
    DRM_RESULT dr                 = DRM_SUCCESS;
    DRM_DWORD  cbData             = 0;
    DRM_DWORD  cbEncoded         = 0;
    DRM_STRING dstrRevocationData = EMPTY_DRM_STRING;


    ChkArg( f_pdstrList  != NULL 
         && f_pdwVersion != NULL );

       
    DSTR_FROM_PB( &dstrRevocationData, 
                  PB_DSTR( f_pdstrList ), 
                  CB_DSTR( f_pdstrList ) );

    cbData = CB_BASE64_DECODE(dstrRevocationData.cchString);


    ChkDR( DRM_B64_DecodeW( (DRM_CONST_STRING *) &dstrRevocationData, 
                         &cbData, 
                          NULL, 
                          DRM_BASE64_DECODE_IN_PLACE ) );
    
    ChkDR( _VerifyBinaryWMDRMNETSignature( (DRM_BYTE*)dstrRevocationData.pwszString,
                                        cbData,
                                        f_pdwVersion ) );

    cbEncoded = dstrRevocationData.cchString;
    ChkDR( DRM_B64_EncodeW( (DRM_BYTE*)dstrRevocationData.pwszString, 
                         cbData, 
                         dstrRevocationData.pwszString, 
                        &cbEncoded,
                         0 ) );
    
 ErrorExit:

    /*
     * If verification failed, the CRL may be in binary form and could
     * could mess up XML parsing later on, so we overwrite it with spaces.
     */
    if( DRM_FAILED( dr ) )
    {
        DRM_BYT_SetBytes( dstrRevocationData.pwszString, 
                    0, 
                    dstrRevocationData.cchString, 
                    ' ' );
    }
    
    return dr;
} 

/******************************************************************************
** 
** Function :   _UpdateWMDRMNETRevocationList
** 
** Synopsis :   updates the WMDRMNET revocation list in the secure store
** 
** Arguments :  
** [f_pcontextCrypto]      -- 
** [f_pcontextSST]         -- 
** [f_rgbPasswordSST]      -- created with the BBX hash
** [f_pbRevocationList]    -- input buffer for exclusion list
** [f_cbRevocationList]    -- buffer size
** [f_pdstrRevocationList] -- the new DCRL
** [f_pID]                 -- DRM_ID used to store this list in secure store
** [f_pcontextHDS]         -- initialized HDS context

** Returns :    
** 
** Notes :      
** 
******************************************************************************/
static DRM_RESULT _UpdateWMDRMNETRevocationList(
    IN          DRM_CRYPTO_CONTEXT   *f_pcontextCrypto,
    IN          DRM_SECSTORE_CONTEXT *f_pcontextSST,
    IN          DRM_BYTE              f_rgbPassword[__CB_DECL(SHA_DIGEST_LEN)],
    IN          DRM_BYTE             *f_pbRevocationList,
    IN          DRM_DWORD             f_cbRevocationList,
    IN          DRM_BYTE             *f_pbRevocationBuffer,
    IN          DRM_DWORD             f_cbRevocationBuffer,
    IN  const   DRM_ID               *f_pID,
    IN  const   PUBKEY               *f_ppubkey,
    IN          DRM_HDS_CONTEXT      *f_pcontextHDS )
{
    DRM_RESULT       dr           = DRM_SUCCESS;        
    DRM_DWORD        idSerialNew  = 0,
                     idSerialOld  = 0,
                     cbReturned   = 0;
    DRM_CONST_STRING dstrTemplate  = EMPTY_DRM_STRING;
    DRM_CONST_STRING dstrList       = EMPTY_DRM_STRING;

    ChkArg(   f_pcontextCrypto   != NULL
           && f_pcontextSST      != NULL
           && f_rgbPassword      != NULL
           && f_pID              != NULL
           && f_pcontextHDS      != NULL
           && f_pbRevocationList           != NULL
           && f_pbRevocationBuffer != NULL
           && f_cbRevocationBuffer > 0
           && f_cbRevocationList > 0 );  


    DSTR_FROM_PB( &dstrList, f_pbRevocationList, f_cbRevocationList );
    
    idSerialNew = DRM_NO_PREVIOUS_CRL;
    idSerialOld = DRM_NO_PREVIOUS_CRL;
    if ( IsWrappedCRL( &dstrList ) )
    {
        ChkDR( _VerifyRevocationList( f_pcontextCrypto, 
                                     &dstrList, 
                                      f_ppubkey,
                                      &idSerialNew ) );
        
        ChkDR( DRM_XML_GetSubNode( &dstrList, 
                                  &g_dstrTemplate, 
                                   NULL, 
                                   NULL, 
                                   0, 
                                   NULL,
                                  &dstrTemplate,
                                   1 ) );

        /* We will store the unwrapped CRL in decoded form */
        ChkDR( DRM_B64_DecodeW( (const DRM_CONST_STRING*)&dstrTemplate,
                               &f_cbRevocationList,
                               NULL,
                               DRM_BASE64_DECODE_IN_PLACE ) );

        f_pbRevocationList = (DRM_BYTE*)dstrTemplate.pwszString;

    }
    else
    {
        ChkDR( _VerifyBinaryWMDRMNETSignature( f_pbRevocationList,
                                                   f_cbRevocationList,
                                                  &idSerialNew ) );
    }


    cbReturned = f_cbRevocationBuffer;
    /* get and verify the current SST image
     */
    dr = _GetSSTRevocationList( f_pID, 
                                f_pcontextCrypto, 
                                f_pcontextSST,
                                f_pcontextHDS,
                                f_rgbPassword,
                                f_ppubkey,
                                f_pbRevocationBuffer,
                               &cbReturned,
                               &idSerialOld );


    /* This can be the first Revocation list */

    if ( dr == DRM_E_FILENOTFOUND 
        || ( ( idSerialOld == DRM_NO_PREVIOUS_CRL ) && DRM_SUCCEEDED( dr ) )
        || ( ( idSerialOld < idSerialNew ) && DRM_SUCCEEDED( dr ) ) )
    {
        
        /* store the new list. */
        ChkDR( DRM_SST_SetData( f_pcontextSST, 
                               f_pID, 
                               NULL,
                               f_rgbPassword,
                               SECURE_STORE_GLOBAL_DATA,
                               f_pcontextHDS, 
                               f_pbRevocationList, 
                               f_cbRevocationList ) );
    }
    else
    {
        ChkDR( dr );    
    }

ErrorExit:

    return dr;
} 


/******************************************************************************
** 
** Function :   DRM_RVK_UpdateWMDRMNETRevocationList
** 
** Synopsis :   updates the WMDRMNET revocation list in the secure store
** 
** Arguments :  
** [f_pcontextCrypto]      -- 
** [f_pcontextSST]         -- 
** [f_rgbPasswordSST]      -- created with the BBX hash
** [f_pbRevocationList]    -- input buffer for exclusion list
** [f_cbRevocationList]    -- buffer size
** [f_pdstrRevocationList] -- the new DCRL
** [f_pID]                 -- DRM_ID used to store this list in secure store
** [f_pcontextHDS]         -- initialized HDS context

** Returns :    
** 
** Notes :      
** 
******************************************************************************/
DRM_RESULT DRM_RVK_UpdateWMDRMNETRevocationList(
    IN          DRM_CRYPTO_CONTEXT   *f_pcontextCrypto,
    IN          DRM_SECSTORE_CONTEXT *f_pcontextSST,
    IN          DRM_BYTE              f_rgbPassword[__CB_DECL(SHA_DIGEST_LEN)],
    IN          DRM_CHAR             *f_rgchBase,
    IN  const   DRM_SUBSTRING        *f_pdasstrRevocationList,
    IN          DRM_BYTE             *f_pbRevocationBuffer,
    IN          DRM_DWORD             f_cbRevocationBuffer,
    IN  const   DRM_ID               *f_pID,
    IN  const   PUBKEY               *f_ppubkey,
    IN          DRM_HDS_CONTEXT      *f_pcontextHDS )
{
    DRM_RESULT       dr           = DRM_SUCCESS;        
    DRM_DWORD        cbDecoded    = 0;
    DRM_BYTE        *pbDecoded   = NULL;

    ChkArg(   f_pcontextCrypto   != NULL
           && f_pcontextSST      != NULL
           && f_rgbPassword      != NULL
           && f_pID              != NULL
           && f_pcontextHDS      != NULL
           && f_rgchBase           != NULL
           && f_pbRevocationBuffer != NULL
           && f_cbRevocationBuffer > 0
           && f_pdasstrRevocationList != NULL
           && f_pdasstrRevocationList->m_cch > 0 );  


     /* pbOffset will point within the base64 revocation list we were passed,
       such that after base64 decoding the end of the decoded data will co-incide with
       the end of the buffer we were given, minus one byte.

       This is because we are giving the B64 decoder overlapping memory for source and desitination,
       and we can't use the DECODE_IN_PLACE flag because this is ANSI where the offset may not coincide
       with a word boundary as required on 16-bit platforms.
     */

     pbDecoded = (DRM_BYTE*)f_rgchBase
                        + __CB_DECL( f_pdasstrRevocationList->m_ich + f_pdasstrRevocationList->m_cch - CB_BASE64_DECODE( f_pdasstrRevocationList->m_cch ) - 1 )
                        + ( ( f_pdasstrRevocationList->m_ich + f_pdasstrRevocationList->m_cch - CB_BASE64_DECODE( f_pdasstrRevocationList->m_cch ) ) % CB_NATIVE_BYTE );

	cbDecoded = CB_BASE64_DECODE( f_pdasstrRevocationList->m_cch ) + 1 - ( ( f_pdasstrRevocationList->m_ich + f_pdasstrRevocationList->m_cch - CB_BASE64_DECODE( f_pdasstrRevocationList->m_cch ) ) % CB_NATIVE_BYTE );

    /* decode the XML for the new exclusion list in place */
    ChkDR( DRM_B64_DecodeA( f_rgchBase,
                            f_pdasstrRevocationList, 
                           &cbDecoded, 
                            pbDecoded, 
                            0 ) );

    ChkDR( _UpdateWMDRMNETRevocationList( f_pcontextCrypto, 
                                    f_pcontextSST, 
                                    f_rgbPassword, 
                                    pbDecoded, 
                                    cbDecoded, 
                                    f_pbRevocationBuffer,
                                    f_cbRevocationBuffer, 
                                    f_pID, 
                                    f_ppubkey, 
                                    f_pcontextHDS ) );

    
ErrorExit:
    if( ( f_rgchBase != NULL ) 
        && ( f_pdasstrRevocationList != NULL )
        && ( f_pdasstrRevocationList->m_cch != 0 ) )
    {
        /* Since we decoded in-place, we need to clean up otherwise the XML parsing may
         * not work properly later on. So we have to overwrite our binary with spaces.
         *
         * NOTE: This means that we CANNOT process the same revocation list in the same
         * license response buffer twice since we're overwriting the buffer we were given
         */
        DRM_BYT_SetBytes( f_rgchBase, f_pdasstrRevocationList->m_ich, f_pdasstrRevocationList->m_cch, ' ');
    }
    return dr;
}

/*****************************************************************************
** Function: DRM_RVK_GetWMDRMNETList
**
** Synopsis: gets the current link protection list from the secure store
**
** Arguments:
** [f_pcontextCRYP]   -- a buffer
** [f_pcontextSST]    -- a buffer
** [f_pcontextHDS]    -- initialized HDS context
** [f_rgbPasswordSST] -- created with the BBX hash
** [f_pbRevocationData]  -- output buffer for exclusion list; NULL to request
**                       required size
** [f_pcbRevocationData] -- DRM_DWORD to hold max buffer size on in, bytes 
**                       actually used on out
** [f_pidSerial]      -- receives numerical representation of <INDEX> tag
*****************************************************************************/
DRM_RESULT DRM_API DRM_RVK_GetWMDRMNETList(
    IN      DRM_CRYPTO_CONTEXT   *f_pcontextCRYP, 
    IN      DRM_SECSTORE_CONTEXT *f_pcontextSST,
    IN      DRM_HDS_CONTEXT      *f_pcontextHDS,
    IN      DRM_BYTE              f_rgbPassword [__CB_DECL(SHA_DIGEST_LEN)],
        OUT DRM_BYTE             *f_pbRevocationData,
    IN  OUT DRM_DWORD            *f_pcbRevocationData,
        OUT DRM_DWORD            *f_pidSerial)
{
    DRM_RESULT          dr = DRM_SUCCESS;

    ChkArg(   f_pcontextCRYP        != NULL
           && f_pcontextSST         != NULL
           && f_pcontextHDS         != NULL
           && f_rgbPassword         != NULL
           && f_pcbRevocationData   != NULL
           && f_pidSerial           != NULL );
    
    dr = _GetSSTRevocationList( &g_lidWMDRMNET_Revocation, 
                                  f_pcontextCRYP, 
                                  f_pcontextSST,
                                  f_pcontextHDS,
                                  f_rgbPassword,
                                  &g_pubkeyCardeaRevocation,
                                  f_pbRevocationData,
                                  f_pcbRevocationData,
                                f_pidSerial );
    if ( dr == DRM_E_FILENOTFOUND
      || dr == DRM_E_HDSSLOTNOTFOUND )
    {
        dr                   = DRM_SUCCESS;
        *f_pidSerial         = 0;
        *f_pcbRevocationData = 0;
        goto ErrorExit;
    }
    ChkDR(dr);
    
ErrorExit:
    return dr;
}

#endif 

#if DRM_SUPPORT_DEVICE_REVOCATION || DRM_SUPPORT_WMDRMNET || DRM_SUPPORT_REVOCATION || DRM_SUPPORT_APP_REVOCATION

typedef struct _tagRevDispatchEntry
{
    DRM_REVOCATION_TYPE_ENUM        eType;
    const DRM_ANSI_CONST_STRING *pdastrType;
    const DRM_LID               *plid;
    const PUBKEY                *ppubkey;
} REVDISPATCHENTRY;

/* dispatch table for various revocation types */
/* note: remove this conditional and all like it if any revocation types are entered 
         feature-independently
         likewise, extend this conditional and all like it if this list is extended with other 
         feature-dependent revocation types */

REVDISPATCHENTRY g_arevDispatch [] =
{
#if DRM_SUPPORT_APP_REVOCATION
    { WM_DRM_REVOCATION_TYPE_APP,     &g_dastrRevocationGuidApp,      &g_lidAppRevocation,       &g_pubkeyRevocation},
#endif /* DRM_SUPPORT_APP_REVOCATION */

#if DRM_SUPPORT_DEVICE_REVOCATION
    { WM_DRM_REVOCATION_TYPE_WMDRMPD, &g_dastrRevocationGuidDevice,   &g_lidDeviceRevocation,    &g_pubkeyDeviceRevocation},
#endif /* DRM_SUPPORT_DEVICE_REVOCATION */

#if DRM_SUPPORT_WMDRMNET
    { WM_DRM_REVOCATION_TYPE_WMDRMND, &g_dastrRevocationGuidWMDRMNET, &g_lidWMDRMNET_Revocation, &g_pubkeyCardeaRevocation},
#endif /* DRM_SUPPORT_WMDRMNET */
};

#endif

    /*
    // The CRL has the following format:
    //
    // +--------------------+------------------------------------------+
    // | SECTIONS           | FIELDS                                   |
    // +--------------------+------------------------------------------+
    // | Header             | 32-bit CRL Version                       |
    // |                    +------------------------------------------+
    // |                    | 32-bit Number of Entries                 |
    // +--------------------+------------------------------------------+
    // | Revocation Entries | Multiple 160-bit Revocation Entries      |
    // +--------------------+------------------------------------------+
    // | Certificate        | 32-bit Certificate Length                |
    // |                    +------------------------------------------+
    // |                    | Variable Length Certificate              |
    // +--------------------+------------------------------------------+
    // | Signature          | 8-bit Signature Type                     |
    // |                    +------------------------------------------+
    // |                    | 16-bit Signature Length                  |
    // |                    +------------------------------------------+
    // |                    | Variable Length Signature                |
    // +--------------------+------------------------------------------+
    */


#if DRM_SUPPORT_REVOCATION
/**********************************************************************
**
** Function:    _VerifyCRLSignature
**
** Synopsis:    Verifies the CRL signature.
**
** Arguments:    
**              [f_pbSignedBytes]        - Specifies the bytes that are signed
**              [f_cbSignedBytes]        - Specifies the size of the signed bytes
**              [f_pbSignature]          - Specifies the signature
**              [f_cbSignature]          - Specifies the size signature
**              [f_pbCertificate]        - Specifies the certificate chain used to find signing key
**              [f_cbCertificate]        - Specifies the size certificate chain used to find signing key
**
** Returns:     DRM_SUCCESS if the signature is valid
**              DRM_XMLNOTFOUND if unable to locate signing key
**              passes return codes from other failures up to caller
**
**********************************************************************/
DRM_RESULT DRM_API _VerifyCRLSignature (DRM_BYTE  *f_pbSignedBytes,
                                              DRM_DWORD     f_cbSignedBytes,
                                              DRM_BYTE     *f_pbSignature,
                                              DRM_DWORD     f_cbSignature,
                                              DRM_SUBSTRING f_dasstrCertificate)
{
    DRM_RESULT          dr = DRM_SUCCESS;
    DRM_RSA_PUBLIC_KEY *pPubKey = NULL;
    DRM_SUBSTRING       dasstrSignedBytes;
    

    dasstrSignedBytes.m_ich = 0;
    dasstrSignedBytes.m_cch = f_cbSignedBytes;

    /* validate certificate chain */
    dr = _ValidateCertificate( (DRM_CHAR *) f_pbSignedBytes, f_dasstrCertificate, NULL );
    if( DRM_FAILED(dr) )
    {
        TRACE(( "_VerifyCRLSignature: Invalid Certificate\n" ));
        ChkDR( dr );
    }

    /* find certificate that signed CRL */
    {
        DRM_SUBSTRING       dasstrCollection = f_dasstrCertificate;
        DRM_SUBSTRING       dasstrData = EMPTY_DRM_SUBSTRING;
        DRM_DWORD           cNodes;
        DRM_DWORD           iNode;


        ChkDR( DRM_XML_GetNodeA( (const DRM_CHAR *) f_pbSignedBytes, &dasstrCollection, &g_dastrTagCertificateCollection, NULL, NULL, 0, NULL, &dasstrData ));

        /* enumerate c:Certificate nodes */

        ChkDR(DRM_XML_CountMatchingNodesA( (const DRM_CHAR *) f_pbSignedBytes,
                                        &dasstrData,
                                        &g_dastrTagWMDRMCertificate,
                                        NULL,
                                        NULL,
                                        &cNodes));

        ChkArg(cNodes >= 1
            && cNodes <= DRM_CERTIFICATE_COLLECTION_MAX);

        for (iNode = 0; iNode < cNodes; iNode++)
        {
            DRM_SUBSTRING dasstrCertificate = EMPTY_DRM_STRING;
            DRM_SUBSTRING dasstrTag         = EMPTY_DRM_STRING;
            DRM_SUBSTRING rgdasstrAttrs [3]   = { 0 };
            DRM_DWORD     cAttrEntries        = NO_OF(rgdasstrAttrs);
            DRM_DWORD     iAttr;

            ChkDR(DRM_XML_EnumNextNodeA( (const DRM_CHAR *) f_pbSignedBytes,
                                        &dasstrData,
                                         iNode,
                                        &dasstrTag,
                                        &dasstrCertificate,
                                         NULL,
                                         NULL,
                                         NULL));

            /* there should be nothing but <c:Certificate>s in <CertificateCollection> */

            ChkArg(DRM_UTL_DASSTRStringsEqual( (const DRM_CHAR *) f_pbSignedBytes,
                                              &dasstrTag,
                                              &g_dastrTagWMDRMCertificate));

            /* Check if cert signed CRL, and if so, get public key */
            ChkDR( DRM_WCP_GetAttribute( (const DRM_CHAR *) f_pbSignedBytes,
                                        &dasstrCertificate,
                                         DRM_WMDRM_CERT_ATTR_KEY_USAGE,
                                         rgdasstrAttrs,
                                        &cAttrEntries ) );

            for( iAttr = 0; iAttr < cAttrEntries ; iAttr++ )
            {
                if( DRM_UTL_DASSTRStringsEqual( (const DRM_CHAR *) f_pbSignedBytes, rgdasstrAttrs + iAttr, &g_dastrKeyUsageSignCRL ) )
                {
                    DRM_DWORD cbDestination;
                    DRM_DWORD dwExponent = 0;
                    DRM_BYTE  rgbExponent[4];
                    DRM_BYTE  rgbModulus [__CB_DECL(DRM_CB_RSA_PUBLIC_MOD_1024 * 2 + 8)] = { 0 };

                    cAttrEntries = 3;
                    ChkDR( DRM_WCP_GetAttribute( (const DRM_CHAR *) f_pbSignedBytes,
                                                &dasstrCertificate,
                                                 DRM_WMDRM_CERT_ATTR_PUBLIC_KEY_SELF_RSA,
                                                 rgdasstrAttrs,
                                                &cAttrEntries ) );

                    /*
                    *  Decode Exponent
                    */
                    cbDestination = 4;
                    ChkDR( DRM_B64_DecodeA( (const DRM_CHAR *) f_pbSignedBytes,
                                            rgdasstrAttrs,
                                           &cbDestination,
                                            rgbExponent,
                                            0 ) );
                    PUT_BYTE( rgbExponent, 3, GET_BYTE( rgbExponent, 2 ) );
                    PUT_BYTE( rgbExponent, 2, GET_BYTE( rgbExponent, 1 ) );
                    PUT_BYTE( rgbExponent, 1, GET_BYTE( rgbExponent, 0 ) );
                    PUT_BYTE( rgbExponent, 0, 0 );
                    NETWORKBYTES_TO_DWORD( dwExponent, rgbExponent, 0 );


                    /*
                    *  Decode Modulus
                    */
                    cbDestination = SIZEOF(rgbModulus);
                    ChkDR( DRM_B64_DecodeA( (const DRM_CHAR *) f_pbSignedBytes,
                                            rgdasstrAttrs + 1,
                                           &cbDestination,
                                            rgbModulus,
                                            0 ) );

                    ChkDR( OEM_DrmRsaSetPublicKey( dwExponent, rgbModulus, cbDestination, &pPubKey ) );

                    break;
                }
            }

            if( pPubKey != NULL )
            {
                break;
            }
        }

        if( pPubKey == NULL )
        {
            TRACE(( "_VerifyCRLSignature: Couldn't find public key for CRL signer\n" ));
            ChkDR( DRM_E_XMLNOTFOUND );
        }
    }


    dr = DrmRsaVerify( (DRM_CHAR*) f_pbSignedBytes, &dasstrSignedBytes, pPubKey, f_pbSignature, f_cbSignature );
    
    if( DRM_FAILED(dr) )
    {
        TRACE(( "_VerifyCRLSignature: Signature Invalid\n" ));
        ChkDR( dr );
    }

ErrorExit:

    if ( pPubKey != NULL )
    {
        OEM_DrmRsaReleaseKey( pPubKey );
    }

    return dr;
}
#endif

#if DRM_SUPPORT_WMDRMNET
static DRM_RESULT DRM_API _VerifyBinaryWMDRMNETSignature(
    IN     DRM_BYTE  *f_pbData,
    IN     DRM_DWORD  f_cbData,
    IN OUT DRM_DWORD *f_pidSerial)
{
    DRM_RESULT          dr              = DRM_SUCCESS;
    DRM_BYTE            bSignatureType  = 0;
    DRM_DWORD           cEntries        = 0;
    DRM_UINT64          cbEntries       = DRM_UI64HL(0, 0);
    DRM_DWORD           cbSigned        = 0;
    DRM_DWORD           dwVersion       = 0;
    DRM_SUBSTRING       dasstrCertificate = EMPTY_DRM_SUBSTRING;
    DRM_DWORD           cbData          = f_cbData;
    DRM_DWORD           ibData          = 0;
    DRM_WORD           cbSignature      = 0;
#if SIXTEEN_BIT_ADDRESSING
    DRM_BYTE            rgbSignature[ __CB_DECL( WMDRMNET_CRL_SIGNATURE_LEN_RSA_SHA1 ) ];
#endif

    /*
    ** Get the CRL Version
    */

    ChkArg(f_cbData    >= SIZEOF(DRM_DWORD)
        && f_pbData    != NULL
        && f_pidSerial != NULL);

    NETWORKBYTES_TO_DWORD( dwVersion, f_pbData, 0 );
    cbSigned += SIZEOF(DRM_DWORD);
    ibData   += SIZEOF(DRM_DWORD);
    cbData   -= SIZEOF(DRM_DWORD);

    /* if the expected version number is given it must match, otherwise its' a query */

    if (*f_pidSerial != DRM_NO_PREVIOUS_CRL)
    {
        ChkArg(dwVersion == *f_pidSerial);
    }
    else
    {
        *f_pidSerial = dwVersion;
    }

    /*
    ** Get the number of entries
    */
    ChkArg(cbData >= SIZEOF(DRM_DWORD));

    NETWORKBYTES_TO_DWORD( cEntries, f_pbData, ibData );
    cbSigned += SIZEOF(DRM_DWORD);
    ibData   += SIZEOF(DRM_DWORD);
    cbData   -= SIZEOF(DRM_DWORD);

    /*
    ** Get the size of the entries
    ** 
    */
    cbEntries = DRM_UI64Mul(DRM_UI64HL( 0, cEntries ), DRM_UI64HL( 0, SIZEOF(WMDRMNET_CRL_ENTRY) ));

    ChkArg(cbData >= DRM_UI64Low32(cbEntries)); /* use qword to prevent arithmetic overflows */


    cbSigned += SIZEOF(WMDRMNET_CRL_ENTRY) * cEntries;
    ibData   += SIZEOF(WMDRMNET_CRL_ENTRY) * cEntries;
    cbData   -= SIZEOF(WMDRMNET_CRL_ENTRY) * cEntries;

    /*
    ** Get the certificate length
    */

    ChkArg(cbData >= SIZEOF(DRM_DWORD));

    NETWORKBYTES_TO_DWORD( dasstrCertificate.m_cch, f_pbData, ibData );

    cbSigned += SIZEOF(DRM_DWORD);
    ibData   += SIZEOF(DRM_DWORD);
    cbData   -= SIZEOF(DRM_DWORD);

    ChkArg(dasstrCertificate.m_cch > 0);

    /*
    ** Get the certificate.  we don't need to copy it, just update
    ** our byte offset to point to the right location.
    */

    ChkArg(cbData >= dasstrCertificate.m_cch);
    dasstrCertificate.m_ich = ibData;


    cbSigned += dasstrCertificate.m_cch;
    ibData   += dasstrCertificate.m_cch;
    cbData   -= dasstrCertificate.m_cch;

    /*
    ** Get the signature type
    */

    ChkArg(cbData >= SIZEOF(DRM_BYTE));

    bSignatureType = GET_BYTE( f_pbData, ibData );

    ibData++;
    cbData--;

    ChkArg( bSignatureType == WMDRMNET_CRL_SIGNATURE_TYPE_RSA_SHA1 );

    /*
    ** get the signature length
    */

    ChkArg(cbData >= SIZEOF(DRM_WORD));

    NETWORKBYTES_TO_WORD( cbSignature, f_pbData, ibData );
    ibData += SIZEOF(DRM_WORD);
    cbData -= SIZEOF(DRM_WORD);

    ChkArg( cbSignature == WMDRMNET_CRL_SIGNATURE_LEN_RSA_SHA1 );

    /*
    ** get the signature and validate it
    */
    ChkArg(cbData >= WMDRMNET_CRL_SIGNATURE_LEN_RSA_SHA1);

#if SIXTEEN_BIT_ADDRESSING
    DRM_BYT_CopyBytes( rgbSignature, 
                      0, 
                      f_pbData, 
                      ibData, 
                      WMDRMNET_CRL_SIGNATURE_LEN_RSA_SHA1 );

    dr = _VerifyCRLSignature( f_pbData,
                            cbSigned,
                            rgbSignature,
                            WMDRMNET_CRL_SIGNATURE_LEN_RSA_SHA1, 
                            dasstrCertificate );
#else
    dr = _VerifyCRLSignature( f_pbData,
                            cbSigned,
                            f_pbData + ibData,
                            WMDRMNET_CRL_SIGNATURE_LEN_RSA_SHA1, 
                            dasstrCertificate );
#endif

    if (DRM_FAILED(dr))
    {
        ChkDR(DRM_E_INVALID_SIGNATURE);
    }

ErrorExit:

    return dr;
}

#endif
#if DRM_SUPPORT_REVOCATION
/**********************************************************************
**
** Function:    _ValidateCertificate
**
** Synopsis:    Validates the device certificate. Optionally returns WMDRM-ND certificate
**
** Arguments:
**              [f_rgchDeviceCertificate]     - Specifies the device certificate
**              [f_cbDeviceCertificate]       - Specifies the size (in bytes) of the Device Certificate
**              [f_pdasstrWMDRMNDCertificate] - Optional, can be NULL.  If not NULL, returns the
**                                              DRM_SUBSTRING pointing to the WMDRM-ND certificate
**
** Returns:     DRM_SUCCESS on success or
**              DRM_E_INVALIDARG if any of the arguments are invalid
**
**********************************************************************/
DRM_RESULT
_ValidateCertificate(
    IN DRM_CHAR          *f_rgchBase,
    IN DRM_SUBSTRING      f_dasstrCertificate,
    IN OUT DRM_SUBSTRING *f_pdasstrWMDRMNDCertificate
    )
{
    DRM_RESULT dr = DRM_SUCCESS;

    ChkArg( f_rgchBase );

    if (DRM_FAILED( DRM_WCP_VerifyCertificateCollection(
                             f_rgchBase,
                            &f_dasstrCertificate,
                             DRM_DSIG_TYPE_WMDRMNET,
                            &g_dastrMSRootPubKey,
                             NULL,
                             0,
                             f_pdasstrWMDRMNDCertificate,
                             NULL ) ) )
    {
        ChkDR(DRM_E_INVALIDDEVICECERTIFICATE);
    }

ErrorExit:
    return dr;
}


/******************************************************************************
**
** Function :   _VerifyRevocationInfo
**
** Synopsis :   Verify signature on the revocation info
**
** Arguments :  f_pbRevInfo  - rev info buffer (already b64 decoded)
**              f_pcbRevInfo - size of rev info buffer
** Returns :
**
** Notes :
**
******************************************************************************/
static DRM_RESULT _VerifyRevocationInfo(
    IN          DRM_BYTE           *f_pbRevInfo,
    IN          DRM_DWORD           f_cbRevInfo,
    OUT         DRM_RLVI           *f_pRLVI)
{
    DRM_RESULT      dr 		= DRM_SUCCESS;
    DRM_DWORD       cbRevInfo 	= f_cbRevInfo;
    DRM_DWORD       cbSignedBytes   = 0;
    DRM_DWORD       cbEntries        = 0;
    DRM_DWORD	    ibRevInfo 	      = 0;
    DRM_SUBSTRING   dasstrCertificate = EMPTY_DRM_SUBSTRING;
#if SIXTEEN_BIT_ADDRESSING
    DRM_BYTE        rgbSignature[ __CB_DECL( RLVI_SIGNATURE_SIZE ) ];
#endif

    ChkArg( f_pbRevInfo != NULL
          && f_pRLVI     != NULL );
    ChkArg( SIZEOF( DRM_RLVI_HEAD ) <= f_cbRevInfo );
    
    MEMSET( f_pRLVI, 0, SIZEOF( DRM_RLVI ) );
    
    /* ID */
    NETWORKBYTES_TO_DWORD( f_pRLVI->head.dwID, f_pbRevInfo, ibRevInfo );
    ChkArg( RLVI_MAGIC_NUM == f_pRLVI->head.dwID );
    ibRevInfo += SIZEOF( DRM_DWORD );

    /* Length */
    NETWORKBYTES_TO_DWORD( f_pRLVI->head.cbSignedBytes, f_pbRevInfo, ibRevInfo );
    ChkArg( SIZEOF( DRM_RLVI_HEAD ) <= f_pRLVI->head.cbSignedBytes );
    ibRevInfo += SIZEOF( DRM_DWORD );
    
    /* Format Version */
    f_pRLVI->head.bFormatVersion = GET_BYTE( f_pbRevInfo, ibRevInfo );
    ChkArg( RLVI_FORMAT_VERSION == f_pRLVI->head.bFormatVersion );
    ibRevInfo += SIZEOF(DRM_BYTE) / CB_NATIVE_BYTE;
    
    /* Reserved */
    f_pRLVI->head.bReserved[0] = GET_BYTE( f_pbRevInfo, ibRevInfo );
    ibRevInfo += SIZEOF(DRM_BYTE) / CB_NATIVE_BYTE;
    f_pRLVI->head.bReserved[1] = GET_BYTE( f_pbRevInfo, ibRevInfo );
    ibRevInfo += SIZEOF(DRM_BYTE) / CB_NATIVE_BYTE;
    f_pRLVI->head.bReserved[2] = GET_BYTE( f_pbRevInfo, ibRevInfo );
    ibRevInfo += SIZEOF(DRM_BYTE) / CB_NATIVE_BYTE;

    /* Sequence Nubmer */
    NETWORKBYTES_TO_DWORD( f_pRLVI->head.dwRIV, f_pbRevInfo, ibRevInfo );
    ibRevInfo += SIZEOF( DRM_DWORD );

    /* Issued Time: not stored in network byte order */
    /* NETWORKBYTES_TO_QWORD( f_pRLVI->head.ftIssuedTime, pbRevInfo, 0 ); */
    ibRevInfo += SIZEOF( DRMFILETIME );

    /* Entry Count */
    NETWORKBYTES_TO_DWORD( f_pRLVI->head.dwRecordCount, f_pbRevInfo, ibRevInfo );
    ibRevInfo += SIZEOF( DRM_DWORD );
    
    /* Entries */
    cbEntries = SIZEOF( DRM_RLVI_RECORD ) * f_pRLVI->head.dwRecordCount;
    ChkArg( f_cbRevInfo - ibRevInfo >= cbEntries + ( SIZEOF(DRM_BYTE) / CB_NATIVE_BYTE ) + RLVI_SIGNATURE_SIZE + SIZEOF( DRM_DWORD ) );
    f_pRLVI->ibEntries = ibRevInfo;
    ibRevInfo += cbEntries;
    
    cbSignedBytes = ibRevInfo;
    
    /* Signature Type */
    f_pRLVI->signature.bSignatureType = GET_BYTE( f_pbRevInfo, ibRevInfo );
    ibRevInfo += SIZEOF(DRM_BYTE) / CB_NATIVE_BYTE;
    ChkArg( RLVI_SIGNATURE_TYPE == f_pRLVI->signature.bSignatureType );
    ChkArg( RLVI_SIGNATURE_SIZE + SIZEOF(DRM_DWORD) <= (cbRevInfo - ibRevInfo) );
    
    /* Signature */
    f_pRLVI->signature.ibSignature = ibRevInfo;
#if SIXTEEN_BIT_ADDRESSING
    DRM_BYT_CopyBytes( rgbSignature, 0, f_pbRevInfo, ibRevInfo, RLVI_SIGNATURE_SIZE );
#endif
    ibRevInfo += RLVI_SIGNATURE_SIZE;
    
    
    /* Certificate Chain Length */
    NETWORKBYTES_TO_DWORD( f_pRLVI->certchain.cbCertChain, f_pbRevInfo, ibRevInfo );
    ibRevInfo += SIZEOF( DRM_DWORD );
    
    /* Certificate Chain */
    ChkArg( f_cbRevInfo - ibRevInfo >= f_pRLVI->certchain.cbCertChain );
    f_pRLVI->certchain.ibCertChain = ibRevInfo;
    ibRevInfo += f_pRLVI->certchain.cbCertChain;

    /* Verify Signature */

    dasstrCertificate.m_ich = f_pRLVI->certchain.ibCertChain;
    dasstrCertificate.m_cch = f_pRLVI->certchain.cbCertChain;

#if SIXTEEN_BIT_ADDRESSING
    ChkDR( _VerifyCRLSignature(f_pbRevInfo,
                                  cbSignedBytes,
                                  rgbSignature,
                                  RLVI_SIGNATURE_SIZE,
                                  dasstrCertificate ) );
#else
    ChkDR( _VerifyCRLSignature(f_pbRevInfo,
                                  cbSignedBytes,
                                  f_pbRevInfo + f_pRLVI->signature.ibSignature,
                                  RLVI_SIGNATURE_SIZE,
                                  dasstrCertificate ) );
#endif

 ErrorExit:
    
    return dr;
}
#endif
/*****************************************************************************
** Function: DRM_RVK_GetCurrentRevocationInfo
**
** Synopsis: gets the current device exclusion list from the secure store
**
** Arguments:
** [f_pcontextSST]       -- just a buffer
** [f_pcontextHDS]       -- initialized* HDS context
** [f_rgbPasswordSST]    -- created with the BBX hash
** [f_pbBuffer]          -- output buffer for exclusion list; NULL to request 
**                          required size
** [f_pcbBuffer]         -- DRM_DWORD to hold max buffer size on in, bytes actually 
**                          used on out
*****************************************************************************/
#if DRM_SUPPORT_REVOCATION
DRM_RESULT DRM_API DRM_RVK_GetCurrentRevocationInfo(
    IN     DRM_SECSTORE_CONTEXT *f_pContextSST,
    IN     DRM_BB_CONTEXT       *f_pContextBB,
    IN     DRM_HDS_CONTEXT      *f_pContextHDS,
       OUT DRM_BYTE             *f_pbBuffer,
    IN OUT DRM_DWORD            *f_pcbBuffer,
       OUT DRM_RLVI             *f_pRLVI)
{

    DRM_RESULT                    dr        = DRM_SUCCESS;
    DRM_BYTE                      rgbPassword [__CB_DECL(SHA_DIGEST_LEN)] = { 0x00 };

    ChkArg ( f_pRLVI != NULL 
        &&   f_pContextSST != NULL
        &&   f_pContextHDS != NULL
        &&   f_pContextBB  != NULL );

    ChkDR( DRM_SST_CreateGlobalStorePassword( rgbPassword, (DRM_BYTE *) f_pContextBB ) );

    ChkDR( DRM_SST_OpenKeyTokens( f_pContextSST, 
                                  (DRM_ID *) &g_rgbSecStoreGlobalName, 
                                  NULL,
                                  rgbPassword, 
                                  DRM_SECURE_STORE_CREATE_IF_NOT_EXISTS,
                                  SECURE_STORE_GLOBAL_DATA, 
                                  f_pContextHDS ) );

    ChkDR( _CreateRevocationStorePassword( f_pContextBB, rgbPassword ) );
    
    dr = DRM_SST_GetData(f_pContextSST,  
                         &g_lidRevocationInfo,
                         NULL,
                         rgbPassword,
                         SECURE_STORE_GLOBAL_DATA,
                         f_pContextHDS, 
                         f_pbBuffer, 
                         f_pcbBuffer);
    if ( dr == DRM_E_FILENOTFOUND
      || dr == DRM_E_HDSSLOTNOTFOUND )
    {
        /* if there's no revocation info current stored, fake out the RIV so the new info can be stored */
        MEMSET( f_pRLVI, 0, SIZEOF( DRM_RLVI ) );
        f_pRLVI->head.dwRIV = 0;
        *f_pcbBuffer = 0;
        dr = DRM_SUCCESS;
        goto ErrorExit;
    }
    ChkDR(dr);

    /* decode the revocation info */

    ChkDR( _VerifyRevocationInfo(f_pbBuffer, *f_pcbBuffer, f_pRLVI) );
    
ErrorExit:
    if ( f_pContextSST && f_pContextHDS )
    {
        f_pContextSST->fInited = TRUE;
        DRM_SST_CloseKey( f_pContextSST, f_pContextHDS );
    }

    return dr;
}


DRM_RESULT DRM_RVK_StoreRevocationLists(
    IN DRM_LICEVAL_CONTEXT   *f_pContextLEVL,
    IN DRM_HDS_CONTEXT       *f_pContextHDS,
    IN DRM_SECSTORE_CONTEXT  *f_pContextSST,
    IN DRM_BB_CONTEXT        *f_pContextBB,
    IN DRM_DWORD              f_cRevocationLists,
    IN DRM_RVK_LIST          *f_pRevocationLists )
{
    DRM_RESULT    dr = DRM_SUCCESS;
    DRM_DWORD     i = 0;
    DRM_BYTE      rgbPassword [__CB_DECL(SHA_DIGEST_LEN)]  = { 0x00 };


    ChkArg( f_cRevocationLists > 0
     && f_pRevocationLists != NULL
     && f_pContextLEVL != NULL
     && f_pContextHDS  != NULL 
     && f_pContextSST  != NULL
     && f_pContextBB   != NULL);


    ChkDR( DRM_SST_CreateGlobalStorePassword( rgbPassword, (DRM_BYTE *) f_pContextBB ) );
        
    ChkDR( DRM_SST_OpenKeyTokens( f_pContextSST, 
                                  (DRM_ID *) &g_rgbSecStoreGlobalName, 
                                  NULL,
                                  rgbPassword, 
                                  DRM_SECURE_STORE_CREATE_IF_NOT_EXISTS,
                                  SECURE_STORE_GLOBAL_DATA, 
                                  f_pContextHDS ) );
        
    ChkDR( _CreateRevocationStorePassword( f_pContextBB, rgbPassword ) );

    /* Process the CRLs first */
    for ( i = 0; i < f_cRevocationLists; i++ )
    {
        if( f_pRevocationLists[i].cbRevocationList == 0 )
        {
            /* Ignore zero length revocation lists */
            continue;
        }
        
        #if DRM_SUPPORT_APP_REVOCATION
        if ( 0 == MEMCMP(&(f_pRevocationLists[i].guidRevocationList), &g_guidRevocationTypeApp, SIZEOF(DRM_GUID)) )
        {
            DRM_DWORD cbBuffer = 0;
            
            /* Ensure latest app crl version is up to date */
            cbBuffer = f_pContextLEVL->cbRevocationBuffer;
            ChkDR( DRM_RVK_GetCurrentAppRevocationList(f_pContextLEVL->pcontextSSTRevocation,
                                                    rgbPassword,
                                                    f_pContextLEVL->pbRevocationBuffer,
                                                   &cbBuffer,
                                                    f_pContextHDS, 
                                                   &f_pContextLEVL->idCRLsCurrent.app ) );
    
            ChkDR( _UpdateAppRevocationList( &f_pContextBB->CryptoContext,
                        f_pContextSST,
                        rgbPassword,
                        f_pRevocationLists[i].pbRevocationList,
                        f_pRevocationLists[i].cbRevocationList,
                        f_pContextHDS,
                       &f_pContextLEVL->appcert,
                        f_pContextLEVL->idCRLsCurrent.app,
                       &f_pContextLEVL->fUpdatedRevocationList ) );

            continue;
        }
        #endif

        #if DRM_SUPPORT_DEVICE_REVOCATION
        if ( 0 == MEMCMP(&(f_pRevocationLists[i].guidRevocationList), &g_guidRevocationTypeDevice, SIZEOF(DRM_GUID)) )
        {
            ChkDR( _UpdateRevocationList( &f_pContextBB->CryptoContext, 
                                          f_pContextSST,
                                          rgbPassword,
                                          f_pRevocationLists[i].pbRevocationList,
                                          f_pRevocationLists[i].cbRevocationList,
                                          f_pContextLEVL->pbRevocationBuffer,
                                          f_pContextLEVL->cbRevocationBuffer,
                                         &g_lidDeviceRevocation,
                                         &g_pubkeyDeviceRevocation,
                                          f_pContextHDS ) );
            continue;
        }
        #endif

        #if DRM_SUPPORT_WMDRMNET
        if ( 0 == MEMCMP(&(f_pRevocationLists[i].guidRevocationList), &g_guidRevocationTypeWMDRMNET, SIZEOF(DRM_GUID)) )
        {
            ChkDR( _UpdateWMDRMNETRevocationList( 
                                          &f_pContextBB->CryptoContext, 
                                          f_pContextSST,
                                          rgbPassword,
                                          f_pRevocationLists[i].pbRevocationList,
                                          f_pRevocationLists[i].cbRevocationList,
                                          f_pContextLEVL->pbRevocationBuffer,
                                          f_pContextLEVL->cbRevocationBuffer,
                                         &g_lidWMDRMNET_Revocation,
                                         &g_pubkeyCardeaRevocation,
                                          f_pContextHDS ) );
            continue;
        }
        #endif
    }

    /* Next process the rev infos */
    for ( i = 0; i < f_cRevocationLists; i++ )
    {
        if( f_pRevocationLists[i].cbRevocationList == 0 )
        {
            /* Ignore zero length revocation infos */
            continue;
        }

        if ( 0 == MEMCMP(&(f_pRevocationLists[i].guidRevocationList), &g_guidRevocationTypeRevInfo, SIZEOF(DRM_GUID)) )
        {
            /* To be symmetric with DRM_MGR_GetRevInfo, we accept base64 encoded revinfos */

            DRM_STRING dstrRevInfo = EMPTY_DRM_STRING;

            dstrRevInfo.pwszString = (DRM_WCHAR*)f_pRevocationLists[i].pbRevocationList;
            dstrRevInfo.cchString = f_pRevocationLists[i].cbRevocationList / SIZEOF( DRM_WCHAR );
            
            ChkDR( DRM_B64_DecodeW( (DRM_CONST_STRING *)&dstrRevInfo, 
                            &f_pRevocationLists[i].cbRevocationList, 
                            NULL, 
                            DRM_BASE64_DECODE_IN_PLACE ) );
            
            ChkDR( DRM_RVK_StoreRevInfo( f_pContextLEVL,
                                    f_pContextHDS,
                                    f_pContextSST,
                                    f_pContextBB,
                                    rgbPassword,
                                    f_pRevocationLists[i].pbRevocationList,
                                    f_pRevocationLists[i].cbRevocationList,
                                    NULL ) );
            continue;
        }
    }
    
ErrorExit:
    ZEROMEM( rgbPassword, SHA_DIGEST_LEN );
    
    return dr;
}

DRM_RESULT DRM_API DRM_RVK_StoreRevInfo (
    IN DRM_LICEVAL_CONTEXT   *f_pContextLEVL,
    IN DRM_HDS_CONTEXT       *f_pContextHDS,
    IN DRM_SECSTORE_CONTEXT  *f_pContextSST,
    IN DRM_BB_CONTEXT        *f_pContextBB,
    IN DRM_BYTE              *f_pbPassword,
    IN DRM_BYTE              *f_pbRevocationInfo,
    IN DRM_DWORD              f_cbRevocationInfo,
    IN DRM_BOOL              *f_fWasStored )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_RLVI       currentRLVI;
    DRM_RLVI       newRLVI;
    DRM_DWORD      cbTmp = 0;
    DRM_DWORD      iRev = 0;
    DRM_BOOL       fAllVersionsMatch = FALSE;

    MEMSET( &newRLVI,       0, SIZEOF( DRM_RLVI ) );
    MEMSET( &currentRLVI,   0, SIZEOF( DRM_RLVI ) );

    ChkArg( f_pbRevocationInfo > 0
     && f_cbRevocationInfo != NULL
     && f_pContextLEVL != NULL
     && f_pContextHDS  != NULL 
     && f_pContextSST  != NULL
     && f_pContextBB   != NULL);

    if( f_fWasStored != NULL )
    {
        *f_fWasStored = FALSE;
    }

    /* Verify the one from the license response is a good one */
    ChkDR( _VerifyRevocationInfo(f_pbRevocationInfo, f_cbRevocationInfo, &newRLVI) );

    cbTmp = f_pContextLEVL->cbRevocationBuffer;
    ChkDR( DRM_RVK_GetCurrentRevocationInfo( f_pContextSST,
                                           f_pContextBB,
                                           f_pContextHDS,
                                           f_pContextLEVL->pbRevocationBuffer, 
                                          &cbTmp, 
                                          &currentRLVI ) );

    /* 
       Compare the indivudual CRL versions in the Rev Info to the ones in the
       store and store only if the versions in the store >= those in Rev Info.
    */

    for ( iRev = 0; iRev < newRLVI.head.dwRecordCount; iRev++ )
    {
        DRM_INT64  qwrlviVersion;
        DRM_DWORD  dwrlviVersion = 0;
        DRM_GUID   rlviGUID;
        DRM_DWORD  crlVersion = 0;

        NETWORKBYTES_TO_QWORD( qwrlviVersion, 
                        f_pbRevocationInfo, 
                        newRLVI.ibEntries + ( iRev * SIZEOF( DRM_RLVI_RECORD ) ) + SIZEOF( DRM_GUID ) ); 

        dwrlviVersion = DRM_I64ToUI32(qwrlviVersion);
        DRM_BYT_CopyBytes( &rlviGUID,
                        0,
                        f_pbRevocationInfo,
                        newRLVI.ibEntries + ( iRev * SIZEOF( DRM_RLVI_RECORD ) ),
                        SIZEOF(DRM_GUID) );
        
        FIX_ENDIAN_DWORD( rlviGUID.Data1 );
        FIX_ENDIAN_WORD( rlviGUID.Data2 );
        FIX_ENDIAN_WORD( rlviGUID.Data3 );

        cbTmp = f_pContextLEVL->cbRevocationBuffer;

#if DRM_SUPPORT_APP_REVOCATION
        if ( 0 == MEMCMP(&rlviGUID, &g_guidRevocationTypeApp, SIZEOF(DRM_GUID)) )
        {
            cbTmp = f_pContextLEVL->cbRevocationBuffer;
            ChkDR( DRM_RVK_GetCurrentAppRevocationList(f_pContextSST,
                                                     f_pbPassword,
                                                     f_pContextLEVL->pbRevocationBuffer,
                                                    &cbTmp,
                                                     f_pContextHDS,
                                                    &crlVersion) );

            if( f_pContextLEVL->idCRLsCurrent.app != crlVersion )
            {
                f_pContextLEVL->idCRLsCurrent.app = crlVersion;
                f_pContextLEVL->fUpdatedRevocationList = TRUE;
            }
            
            if ( crlVersion >= dwrlviVersion )
            {
                fAllVersionsMatch = TRUE;
            }
            else
            {
                fAllVersionsMatch = FALSE;
                break;
            }
        }
#endif /* DRM_SUPPORT_APP_REVOCATION */

#if DRM_SUPPORT_DEVICE_REVOCATION
        if ( 0 == MEMCMP(&rlviGUID, &g_guidRevocationTypeDevice, SIZEOF(DRM_GUID)) )
        {
            cbTmp = f_pContextLEVL->cbRevocationBuffer;
            ChkDR( DRM_RVK_GetDeviceRevocationList(&f_pContextBB->CryptoContext,
                                                 f_pContextSST,
                                                 f_pContextHDS,
                                                 f_pbPassword,
                                                 f_pContextLEVL->pbRevocationBuffer,
                                                &cbTmp,
                                                &crlVersion) );

            if( f_pContextLEVL->idCRLsCurrent.device != crlVersion )
            {
                f_pContextLEVL->idCRLsCurrent.device = crlVersion;
                f_pContextLEVL->fUpdatedRevocationList = TRUE;
            }

            if ( crlVersion >= dwrlviVersion )
            {
                fAllVersionsMatch = TRUE;
                f_pContextLEVL->fUpdatedRevocationList = TRUE;
            }
            else
            {
                fAllVersionsMatch = FALSE;
                break;
            }
        }
#endif /* DRM_SUPPORT_DEVICE_REVOCATION */

#if DRM_SUPPORT_WMDRMNET
        if ( 0 == MEMCMP(&rlviGUID, &g_guidRevocationTypeWMDRMNET, SIZEOF(DRM_GUID)) )
        {
            cbTmp = f_pContextLEVL->cbRevocationBuffer;
            ChkDR( DRM_RVK_GetWMDRMNETList(&f_pContextBB->CryptoContext,
                                         f_pContextSST,
                                         f_pContextHDS,
                                         f_pbPassword,
                                         f_pContextLEVL->pbRevocationBuffer,
                                        &cbTmp,
                                        &crlVersion) );

            if( f_pContextLEVL->idCRLsCurrent.wmdrmnet != crlVersion )
            {
                f_pContextLEVL->idCRLsCurrent.wmdrmnet = crlVersion;
                f_pContextLEVL->fUpdatedRevocationList = TRUE;
            }

            if ( crlVersion >= dwrlviVersion )
            {
                fAllVersionsMatch = TRUE;
                f_pContextLEVL->fUpdatedRevocationList = TRUE;
            }
            else
            {
                fAllVersionsMatch = FALSE;
                break;
            }
        }
#endif /* DRM_SUPPORT_WMDRMNET */
    }
    /* If each stored version matches the corresponding rlvi version, then store the rlvi */
    if ( TRUE == fAllVersionsMatch && newRLVI.head.dwRIV > currentRLVI.head.dwRIV )
    {
        /* Storing binary revocation info */
        ChkDR( DRM_SST_SetData( f_pContextSST,
                                &g_lidRevocationInfo, 
                                NULL,
                                f_pbPassword,
                                SECURE_STORE_GLOBAL_DATA,
                                f_pContextHDS, 
                                f_pbRevocationInfo,
                                f_cbRevocationInfo ) );
    }

    if( f_fWasStored != NULL )
    {
        *f_fWasStored = fAllVersionsMatch;
    }

ErrorExit:
    return dr;

}

/*****************************************************************************
** Function: DRM_RVK_ProcessRevocationInfo
**
** Synopsis: 
** processes the revocation info struct from the license response and updates
** the revinfo and revocations stored on the device if necessary
**
** Arguments:
** [f_pDrmContext]          -- DRM context
** [f_pdastrLicenseResponse]-- license response XML string
*****************************************************************************/

DRM_RESULT DRM_API DRM_RVK_ProcessRevocationInfo (
    IN DRM_LICEVAL_CONTEXT   *f_pContextLEVL,
    IN DRM_HDS_CONTEXT       *f_pContextHDS,
    IN DRM_SECSTORE_CONTEXT  *f_pContextSST,
    IN DRM_BB_CONTEXT        *f_pContextBB,
    IN DRM_ANSI_CONST_STRING *f_pdastrLicenseResponse)
{
    DRM_RESULT     dr = DRM_SUCCESS;
    DRM_BYTE       rgbPassword [__CB_DECL(SHA_DIGEST_LEN)]  = { 0x00 };
    DRM_BYTE      *pbLicenseResponse;
    DRM_SUBSTRING  dasstrLicenseResponse                    = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING  dasstrDataWithoutTags                    = EMPTY_DRM_SUBSTRING;
    DRM_BYTE      *pbNewRevocationInfo                      = NULL;
    DRM_DWORD      cbNewRevocationInfo                      = 0;
    DRM_RLVI       newRLVI;
    DRM_RLVI       currentRLVI;
    DRM_DWORD      iRev;
    DRM_SUBSTRING  dasstrRevocationList                     = EMPTY_DRM_SUBSTRING;
    DRM_DWORD      cbTmp                                    = 0;
    DRM_BOOL       fAllVersionsMatch                        = FALSE;


    MEMSET( &newRLVI,       0, SIZEOF( DRM_RLVI ) );
    MEMSET( &currentRLVI,   0, SIZEOF( DRM_RLVI ) );

    ChkArg( f_pdastrLicenseResponse            != NULL
         && f_pdastrLicenseResponse->pszString != NULL
         && f_pdastrLicenseResponse->cchString != 0 
         && f_pContextLEVL != NULL
         && f_pContextHDS  != NULL 
         && f_pContextSST  != NULL
         && f_pContextBB   != NULL);

    pbLicenseResponse = (DRM_BYTE*)f_pdastrLicenseResponse->pszString;
    dasstrLicenseResponse.m_cch = f_pdastrLicenseResponse->cchString;

    ChkDR( DRM_SST_CreateGlobalStorePassword( rgbPassword, (DRM_BYTE *) f_pContextBB ) );
        
    ChkDR( DRM_SST_OpenKeyTokens( f_pContextSST, 
                                  (DRM_ID *) &g_rgbSecStoreGlobalName, 
                                  NULL,
                                  rgbPassword, 
                                  DRM_SECURE_STORE_CREATE_IF_NOT_EXISTS,
                                  SECURE_STORE_GLOBAL_DATA, 
                                  f_pContextHDS ) );
        
    ChkDR( _CreateRevocationStorePassword( f_pContextBB, rgbPassword ) );

    /* foreach revocation type we care about, get it out of the license response, and store if the version is newer */
    for (iRev = 0; iRev < NO_OF(g_arevDispatch); iRev++)
    {
        dr = _ExtractRevocationList( f_pdastrLicenseResponse, 
                                     g_arevDispatch [iRev].pdastrType, 
                                     &dasstrRevocationList );

        if ( DRM_E_XMLNOTFOUND == dr )
        {
            dr = DRM_SUCCESS;
            continue;
        }
        ChkDR(dr);
        
        switch ( g_arevDispatch[iRev].eType )
        {
#if DRM_SUPPORT_APP_REVOCATION
        case WM_DRM_REVOCATION_TYPE_APP:
            ChkDR( DRM_RVK_UpdateAppRevocationListA( &f_pContextBB->CryptoContext, 
                                                     f_pContextSST,
                                                     rgbPassword,
                                                     f_pContextLEVL->pbRevocationBuffer,
                                                     f_pContextLEVL->cbRevocationBuffer,
                                          (DRM_CHAR*)f_pdastrLicenseResponse->pszString,
                                                    &dasstrRevocationList,
                                                     f_pContextHDS,
                                                    &f_pContextLEVL->appcert,
                                                     f_pContextLEVL->idCRLsCurrent.app,
                                                    &f_pContextLEVL->fUpdatedRevocationList ) );
            break;
#endif /* DRM_SUPPORT_APP_REVOCATION */

#if DRM_SUPPORT_DEVICE_REVOCATION
        case WM_DRM_REVOCATION_TYPE_WMDRMPD:
            ChkDR( DRM_RVK_UpdateRevocationList( &f_pContextBB->CryptoContext, 
                                          f_pContextSST,
                                          rgbPassword,
                               (DRM_CHAR*)f_pdastrLicenseResponse->pszString,
                                         &dasstrRevocationList,
                                          f_pContextLEVL->pbRevocationBuffer,
                                          f_pContextLEVL->cbRevocationBuffer,
                                          g_arevDispatch[iRev].plid,
                                          g_arevDispatch[iRev].ppubkey,
                                          f_pContextHDS ) );
            break;
#endif /* DRM_SUPPORT_DEVICE_REVOCATION */
#if DRM_SUPPORT_WMDRMNET
        case WM_DRM_REVOCATION_TYPE_WMDRMND:
            ChkDR( DRM_RVK_UpdateWMDRMNETRevocationList( 
                                          &f_pContextBB->CryptoContext, 
                                          f_pContextSST,
                                          rgbPassword,
                               (DRM_CHAR*)f_pdastrLicenseResponse->pszString,
                                         &dasstrRevocationList,
                                          f_pContextLEVL->pbRevocationBuffer,
                                          f_pContextLEVL->cbRevocationBuffer,
                                          g_arevDispatch[iRev].plid,
                                          g_arevDispatch[iRev].ppubkey,
                                          f_pContextHDS ) );

            break;
#endif /* DRM_SUPPORT_WMDRMNET */
        default:
            ChkDR(DRM_E_INVALIDARG);
            break;
        }
    }

    /* 
      Process the revocation info struct from the license response after the  
      CRLs in the license response if present are processed.
    */
    
    dr = DRM_XML_GetSubNodeA((DRM_CHAR*)pbLicenseResponse,
                            &dasstrLicenseResponse,
                            &g_dastrTagRevocationInfo, 
                             NULL, 
                             NULL, 
                             0, 
                             NULL,
                            &dasstrDataWithoutTags, 
                             1);
    if ( DRM_E_XMLNOTFOUND == dr )
    {
        dr = DRM_SUCCESS;
        goto ErrorExit;
    }
    ChkDR(dr);

     /* pbOffset will point within the base64 revocation list we were passed,
       such that after base64 decoding the end of the decoded data will co-incide with
       the end of the buffer we were given, minus one byte.

       This is because we are giving the B64 decoder overlapping memory for source and desitination,
       and we can't use the DECODE_IN_PLACE flag because this is ANSI where the offset may not coincide
       with a word boundary as required on 16-bit platforms.
     */

    pbNewRevocationInfo = pbLicenseResponse
                        + __CB_DECL( dasstrDataWithoutTags.m_ich + dasstrDataWithoutTags.m_cch - CB_BASE64_DECODE( dasstrDataWithoutTags.m_cch ) - 1 )
                        + ( ( dasstrDataWithoutTags.m_ich + dasstrDataWithoutTags.m_cch - CB_BASE64_DECODE( dasstrDataWithoutTags.m_cch ) ) % CB_NATIVE_BYTE );

	cbNewRevocationInfo = CB_BASE64_DECODE( dasstrDataWithoutTags.m_cch ) + 1 - ( ( dasstrDataWithoutTags.m_ich + dasstrDataWithoutTags.m_cch - CB_BASE64_DECODE( dasstrDataWithoutTags.m_cch ) ) % CB_NATIVE_BYTE );


    /* decode the XML for the new revocation info in place */
    ChkDR( DRM_B64_DecodeA( (const DRM_CHAR*)pbLicenseResponse,
                           &dasstrDataWithoutTags, 
                           &cbNewRevocationInfo, 
                            pbNewRevocationInfo, 
                            0 ) );


    /* Verify the one from the license response is a good one */
    ChkDR( _VerifyRevocationInfo(pbNewRevocationInfo, cbNewRevocationInfo, &newRLVI) );

    /* Now get the one store in the data store */
    cbTmp = f_pContextLEVL->cbRevocationBuffer;
    ChkDR( DRM_RVK_GetCurrentRevocationInfo( f_pContextSST,
                                           f_pContextBB,
                                           f_pContextHDS,
                                           f_pContextLEVL->pbRevocationBuffer, 
                                          &cbTmp, 
                                          &currentRLVI ) );

    /* 
       Compare the indivudual CRL versions in the Rev Info to the ones in the
       store and store only if the versions in the store >= those in Rev Info.
    */

    for ( iRev = 0; iRev < newRLVI.head.dwRecordCount; iRev++ )
    {
        DRM_INT64  qwrlviVersion;
        DRM_DWORD  dwrlviVersion = 0;
        DRM_GUID   rlviGUID;
        DRM_DWORD  crlVersion = 0;

        NETWORKBYTES_TO_QWORD( qwrlviVersion, 
                        pbNewRevocationInfo, 
                        newRLVI.ibEntries + ( iRev * SIZEOF( DRM_RLVI_RECORD ) ) + SIZEOF( DRM_GUID ) ); 

        dwrlviVersion = DRM_I64ToUI32(qwrlviVersion);
        DRM_BYT_CopyBytes( &rlviGUID,
                        0,
                        pbNewRevocationInfo,
                        newRLVI.ibEntries + ( iRev * SIZEOF( DRM_RLVI_RECORD ) ),
                        SIZEOF(DRM_GUID) );
        
        FIX_ENDIAN_DWORD( rlviGUID.Data1 );
        FIX_ENDIAN_WORD( rlviGUID.Data2 );
        FIX_ENDIAN_WORD( rlviGUID.Data3 );

        cbTmp = f_pContextLEVL->cbRevocationBuffer;

#if DRM_SUPPORT_APP_REVOCATION
        if ( 0 == MEMCMP(&rlviGUID, &g_guidRevocationTypeApp, SIZEOF(DRM_GUID)) )
        {
            ChkDR( DRM_RVK_GetCurrentAppRevocationList(f_pContextSST,
                                                     rgbPassword,
                                                     f_pContextLEVL->pbRevocationBuffer,
                                                    &cbTmp,
                                                     f_pContextHDS,
                                                    &crlVersion) );

            if( f_pContextLEVL->idCRLsCurrent.app != crlVersion )
            {
                f_pContextLEVL->idCRLsCurrent.app = crlVersion;
                f_pContextLEVL->fUpdatedRevocationList = TRUE;
            }
            
            if ( crlVersion >= dwrlviVersion )
            {
                fAllVersionsMatch = TRUE;
            }
            else
            {
                fAllVersionsMatch = FALSE;
                break;
            }
        }
#endif /* DRM_SUPPORT_APP_REVOCATION */

#if DRM_SUPPORT_DEVICE_REVOCATION
        if ( 0 == MEMCMP(&rlviGUID, &g_guidRevocationTypeDevice, SIZEOF(DRM_GUID)) )
        {
            ChkDR( DRM_RVK_GetDeviceRevocationList(&f_pContextBB->CryptoContext,
                                                 f_pContextSST,
                                                 f_pContextHDS,
                                                 rgbPassword,
                                                 f_pContextLEVL->pbRevocationBuffer,
                                                &cbTmp,
                                                &crlVersion) );

            if( f_pContextLEVL->idCRLsCurrent.device != crlVersion )
            {
                f_pContextLEVL->idCRLsCurrent.device = crlVersion;
                f_pContextLEVL->fUpdatedRevocationList = TRUE;
            }

            if ( crlVersion >= dwrlviVersion )
            {
                fAllVersionsMatch = TRUE;
                f_pContextLEVL->fUpdatedRevocationList = TRUE;
            }
            else
            {
                fAllVersionsMatch = FALSE;
                break;
            }
        }
#endif /* DRM_SUPPORT_DEVICE_REVOCATION */

#if DRM_SUPPORT_WMDRMNET
        if ( 0 == MEMCMP(&rlviGUID, &g_guidRevocationTypeWMDRMNET, SIZEOF(DRM_GUID)) )
        {
            ChkDR( DRM_RVK_GetWMDRMNETList(&f_pContextBB->CryptoContext,
                                         f_pContextSST,
                                         f_pContextHDS,
                                         rgbPassword,
                                         f_pContextLEVL->pbRevocationBuffer,
                                        &cbTmp,
                                        &crlVersion) );

            if( f_pContextLEVL->idCRLsCurrent.wmdrmnet != crlVersion )
            {
                f_pContextLEVL->idCRLsCurrent.wmdrmnet = crlVersion;
                f_pContextLEVL->fUpdatedRevocationList = TRUE;
            }

            if ( crlVersion >= dwrlviVersion )
            {
                fAllVersionsMatch = TRUE;
                f_pContextLEVL->fUpdatedRevocationList = TRUE;
            }
            else
            {
                fAllVersionsMatch = FALSE;
                break;
            }
        }
#endif /* DRM_SUPPORT_WMDRMNET */
    }
    /* If each stored version matches the corresponding rlvi version, then store the rlvi */
    if ( TRUE == fAllVersionsMatch && newRLVI.head.dwRIV > currentRLVI.head.dwRIV )
    {
        /* Storing binary revocation info */
        ChkDR( DRM_SST_SetData( f_pContextSST,
                                &g_lidRevocationInfo, 
                                NULL,
                                rgbPassword,
                                SECURE_STORE_GLOBAL_DATA,
                                f_pContextHDS, 
                                pbNewRevocationInfo,
                                cbNewRevocationInfo ) );
    }
    
ErrorExit:

    if( ( dasstrDataWithoutTags.m_cch != 0 )
        && ( f_pdastrLicenseResponse != NULL )
        && ( f_pdastrLicenseResponse->pszString != NULL ))
    {
        /* Since we decoded in-place, we need to clean up otherwise the XML parsing may
         * not work properly later on. So we have to overwrite our binary with spaces.
         *
         * NOTE: This means that we CANNOT process the same revocation list in the same
         * license response buffer twice since we're overwriting the buffer we were given
         */
        DRM_BYT_SetBytes( (DRM_BYTE*)f_pdastrLicenseResponse->pszString, dasstrDataWithoutTags.m_ich, dasstrDataWithoutTags.m_cch, ' ');
    }

    if( dr == DRM_E_BUFFERTOOSMALL )
    {
        dr = DRM_E_REVOCATION_BUFFERTOOSMALL;
    }

    return dr;    
}
#endif /* DRM_SUPPORT_REVOCATION */


