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

#include <drmcommon.h>
#include <drmutilities.h>
#include <drmcrt.h>
#include <drmxmlparser.h>
#include <drmdevcert.h>
#include <drmdes.h>
#include <drmpkcrypto.h>
#include <oemimpl.h>
#include <drmdevcert.h>

#define DRM_CLOCK_SECURE        2
#define DRM_CLOCK_ANTIROLLBACK  1

/******************************************************************************
** 
** Function :   _DCP_VerifyBasicValues
** Synopsis :   Verify the existence of certain required nodes, as well as 
**              validity of the data supplied
******************************************************************************/
static DRM_RESULT _DCP_VerifyBasicValues(
    IN const DRM_CONST_STRING   *f_pdstrDevCert,
    IN       DRM_CRYPTO_CONTEXT *f_pcontextCrypto  )
{
    DRM_RESULT       dr             = DRM_SUCCESS;
    DRM_CONST_STRING dstrValue      = EMPTY_DRM_STRING;
    DRM_LONG         lValue         = 0;
    DRM_LONG         lMaxSecLevel   = 0;
   
    /*
    **  No need to check args in internal function - always checked
    */
    
    /*
    **  Get max SECURITYLEVEL from DAC
    */
    ChkDR( DRM_DCP_GetAttribute( f_pdstrDevCert, 
                                 DRM_DEVCERT_DACSECURITYLEVEL,
                                 NULL,
                                 &dstrValue ) );    
    ChkDR( wcsntol (dstrValue.pwszString, dstrValue.cchString, &lMaxSecLevel));
   
    /*
    **  Get GC security level
    */
    ChkDR( DRM_DCP_GetAttribute( f_pdstrDevCert, 
                                 DRM_DEVCERT_GROUPSECURITYLEVEL,
                                 NULL,
                                 &dstrValue ) );    
    ChkDR( wcsntol (dstrValue.pwszString, dstrValue.cchString, &lValue));
    if ( lValue > lMaxSecLevel )
    {
        ChkDR( DRM_E_INVALIDDEVICECERTIFICATE );
    }
    
    /*
    **  Do we need to check anything else?
    **  TODO: Confirm with PM whether we want any required values in devcert.
    */

ErrorExit:

    if( DRM_FAILED( dr ) )
    {
        dr = DRM_E_INVALIDDEVICECERTIFICATE;
    }

    return dr;
}


/******************************************************************************
** 
** Function :   _DCP_VerifyCert
** 
** Synopsis :   Verify any particular cert in the devcert
** 
** Arguments :  f_pDevCert -
**              f_pcontextCrypto - 
**              f_pdstrPubkey     - pubkey use to verify cert
**              f_eDataLocation   - Attrib to locate the signed data in devcert
**              f_eSignatureLocation - Attrib to locate the signature over given data            
**
** Returns :    
** 
** Notes :      
** 
******************************************************************************/
static DRM_RESULT _DCP_VerifyCert(
    IN const    DRM_CONST_STRING        *f_pdstrDevCert,
    IN          DRM_CRYPTO_CONTEXT      *f_pcontextCrypto,
    IN  const   DRM_CONST_STRING        *f_pdstrPubkey,
    IN          eDRM_DEVCERT_ATTRIBUTES  f_eDataLocation,
    IN          eDRM_DEVCERT_ATTRIBUTES  f_eSignatureLocation )
{
    DRM_RESULT          dr              =   DRM_SUCCESS;
    DRM_CONST_STRING    dstrData        = EMPTY_DRM_STRING;
    DRM_CONST_STRING    dstrSignature   = EMPTY_DRM_STRING;
    DRM_DWORD           cbPubkey        = SIZEOF(PUBKEY);
    DRM_DWORD           cbSignature     = SIZEOF(f_pcontextCrypto->signature);
    /*
    **  No need to check args in internal function - always checked
    */
    
    /*
    **  Get the DEVCERT\CERTIFICATE\DATA SECTION
    */
    ChkDR( DRM_DCP_GetAttribute( f_pdstrDevCert, 
                                 f_eDataLocation,
                                 &dstrData, 
                                 NULL ) );
    
    /*
    **  Decode the key and put it in the crypto context
    */
    ChkDR( DRM_B64_DecodeW(f_pdstrPubkey, 
                          &cbPubkey, 
                           f_pcontextCrypto->pubKey.y, 
                           0) ); 

    /*
    **  Get the signature
    */
    ChkDR( DRM_DCP_GetAttribute( f_pdstrDevCert, 
                                 f_eSignatureLocation,
                                 NULL,
                                 &dstrSignature ) );

    /*
    **  Verify the signature
    */
    ChkDR(DRM_B64_DecodeW( &dstrSignature, 
                           &cbSignature, 
                           f_pcontextCrypto->signature, 
                           0 ) );
    
    if( !DRM_PK_Verify( f_pcontextCrypto->rgbCryptoContext, 
                       &f_pcontextCrypto->pubKey,
                        PB_DSTR(&dstrData), 
                        CB_DSTR(&dstrData),
                        f_pcontextCrypto->signature) )
    {       
        dr = DRM_E_INVALID_SIGNATURE;
    }
    
ErrorExit:
    if( DRM_FAILED( dr ) )
    {
        dr = DRM_E_INVALIDDEVICECERTIFICATETEMPLATE;
    }    
    return dr;
}



/******************************************************************************
** Function :   DRM_DCP_GetAttribute
** Synopsis :   Get attribute from given devcert
** Arguments :  f_pdstrDevCert
**              f_eAttribute  - devcert attr to get
**              f_pdstrNode   - extracted attr node with <node> tag 
**              f_pdstrValue  - extracted attr node value              
******************************************************************************/
DRM_RESULT DRM_API DRM_DCP_GetAttribute(
    IN const DRM_CONST_STRING        *f_pdstrDevCert,          /* devcert */
    IN       eDRM_DEVCERT_ATTRIBUTES  f_eAttribute,            /* devcert attr to get */
       OUT   DRM_CONST_STRING        *f_pdstrNode,             /* extracted attr node with <node> tag */
       OUT   DRM_CONST_STRING        *f_pdstrValue)
{
    DRM_RESULT                   dr                 = DRM_SUCCESS;
    const DRM_CONST_STRING      *pdstrPath          = NULL;
    const DRM_CONST_STRING      *pdstrCertType      = NULL;
    const DRM_CONST_STRING      *pdstrTagSubNode    = NULL;
    DRM_CONST_STRING             dstrSubNode        = EMPTY_DRM_STRING;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_DCP_GetAttribute", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);

    ChkArg( f_pdstrNode != NULL 
        || f_pdstrValue != NULL );
    ChkDRMString( f_pdstrDevCert );

    switch(f_eAttribute)
    {
        /*
        **  Device Cert attributes
        */
        case DRM_DEVCERT_DEVICEPRIVKEY:
            pdstrCertType   = &g_dstrCertTypeDevice;
            pdstrPath       = &g_dstrXPathDeviceKeydata;
            break;            
        case DRM_DEVCERT_DEVICEDATANODE:
            pdstrCertType   = &g_dstrCertTypeDevice;
            pdstrPath       = &g_dstrTagData;
            break;
        case DRM_DEVCERT_DEVICESIGNATURE:
            pdstrCertType   = &g_dstrCertTypeDevice;
            pdstrPath       = &g_dstrTagMSDRMSignature;
            break;         
        case DRM_DEVCERT_DEVICEPUBKEY:
            pdstrCertType   = &g_dstrCertTypeDevice;
            pdstrPath       = &g_dstrXPathPubkeyData; 
            break;  
        case DRM_DEVCERT_SERIALNUMBER: 
            pdstrCertType   = &g_dstrCertTypeDevice;
            pdstrPath       = &g_dstrXPathDataUniqueID; 
            break;        
                        
        /*
        **  Fallback attributes
        */
        case DRM_DEVCERT_COMPATIBILITYVERSION:
            pdstrTagSubNode = &g_dstrFallBack;
            pdstrCertType   = NULL;
            pdstrPath       = &g_dstrTagSecurityVersion;             
            break;            
        case DRM_DEVCERT_COMPATIBILITYCERT:
            pdstrTagSubNode = &g_dstrFallBack;
            pdstrCertType   = NULL;
            pdstrPath       = &g_dstrTagCertificate; 
            break;

        
        /*
        **  GC Attributes
        */
        case DRM_DEVCERT_SECURECLOCK:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrXPathGCSecureClock; 
            break;
        case DRM_DEVCERT_SECURECLOCKURL:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrXPathGCSecureClockURL; 
            break;
        case DRM_DEVCERT_SECURECLOCKPUBKEY:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrXPathGCSecureClockPubKey; 
            break;
        case DRM_DEVCERT_METERING:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrXPathGCMetering; 
            break;
        case DRM_DEVCERT_LICENSEACQUISITIONMODE:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrXPathGCLicenseAcquisitionMode; 
            break;
        case DRM_DEVCERT_LICENSESYNCMODE:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrXPathGCLicenseSyncMode; 
            break;
        case DRM_DEVCERT_SYMMETRICOPTIMIZATIONS:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrXPathGCSymmOptsMode; 
            break;
        case DRM_DEVCERT_ENCRYPTION:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrXPathGCEncryption; 
            break;
        case DRM_DEVCERT_MAXCHAINDEPTH:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrXPathGCMaxChainDepth; 
            break;
        case DRM_DEVCERT_MAXLICENSESIZE:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrXPathGCMaxLicenseSize; 
            break;
        case DRM_DEVCERT_MAXHEADERSIZE:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrXPathGCMaxHeaderSize; 
            break;
        case DRM_DEVCERT_GROUPSECURITYLEVEL:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrXPathTagSecurityLevel; 
            break;
        case DRM_DEVCERT_GROUPCERTPUBKEY:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrXPathPubkeyData; 
            break;
        case DRM_DEVCERT_GROUPCERTDATANODE:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrTagData; 
            break;
        case DRM_DEVCERT_GROUPCERTSIGNATURE:
            pdstrCertType   = &g_dstrCerttypeGroup;
            pdstrPath       = &g_dstrTagMSDRMSignature; 
            break;        
            
        /*
        **  DAC attributes
        */
        case DRM_DEVCERT_DACSECURITYLEVEL:
            pdstrCertType   = &g_dstrCerttypeAuth;
            pdstrPath       = &g_dstrXPathTagSecurityLevel; 
            break;
        case DRM_DEVCERT_SUBJECTID:
            pdstrCertType   = &g_dstrCerttypeAuth;
            pdstrPath       = &g_dstrXPathDACAuthID; 
            break;            
        case DRM_DEVCERT_DACPUBKEY:
            pdstrCertType   = &g_dstrCerttypeAuth;
            pdstrPath       = &g_dstrXPathPubkeyData; 
            break;            
        case DRM_DEVCERT_DACDATANODE:
            pdstrCertType   = &g_dstrCerttypeAuth;
            pdstrPath       = &g_dstrTagData; 
            break;            
        case DRM_DEVCERT_DACSIGNATURE:
            pdstrCertType   = &g_dstrCerttypeAuth;
            pdstrPath       = &g_dstrTagMSDRMSignature; 
            break;                     
        case DRM_DEVCERT_DACROOTPUBKEY:
            pdstrCertType   = &g_dstrCerttypeAuthRoot;
            pdstrPath       = &g_dstrXPathPubkeyData; 
            break;            
        case DRM_DEVCERT_AUTHROOTDATANODE:
            pdstrCertType   = &g_dstrCerttypeAuthRoot;
            pdstrPath       = &g_dstrTagData; 
            break;            
        case DRM_DEVCERT_AUTHROOTSIGNATURE:
            pdstrCertType   = &g_dstrCerttypeAuthRoot;
            pdstrPath       = &g_dstrTagMSDRMSignature; 
            break;            
            
        default:
            ChkDR(DRM_E_INVALIDARG);
    }

    if (pdstrCertType != NULL)
    {        
        ChkDR( DRM_XML_GetSubNode( f_pdstrDevCert, 
                                   &g_dstrTagCertificate, 
                                   &g_dstrAttributeType, 
                                   pdstrCertType, 
                                   0,
                                   &dstrSubNode, 
                                   NULL,
                                   1 ) );           
    }
    else
    {
        ChkDR( DRM_XML_GetSubNode( f_pdstrDevCert, 
                                   pdstrTagSubNode, 
                                   NULL, 
                                   NULL, 
                                   0,
                                   &dstrSubNode, 
                                   NULL,
                                   1 ) );         
    }

    ChkDR( DRM_XML_GetSubNodeByPath(&dstrSubNode, 
                                     pdstrPath, 
                                     NULL, 
                                     NULL, 
                                     f_pdstrNode, 
                                     f_pdstrValue, 
                                     g_wchForwardSlash ) );  
        
ErrorExit:
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_DCP_GetAttribute", g_pwszLeavingFunction);
    return dr;
}


/******************************************************************************
** Function  :   DRM_DCP_VerifyDeviceCert
** Synopsis  :   
** Arguments : 
******************************************************************************/
DRM_RESULT DRM_API DRM_DCP_VerifyDeviceCert(
    IN const DRM_CONST_STRING   *f_pdstrDevCert,
    IN       DRM_DWORD           f_dwVerifyFlags,
    IN       DRM_CRYPTO_CONTEXT *f_pcontextCRYP )
{
    DRM_RESULT       dr         = DRM_SUCCESS;
    DRM_CONST_STRING dstrPubkey = EMPTY_DRM_STRING;
                                         
    /*
    **  Check input
    */
    ChkArg (f_pcontextCRYP != NULL);
    ChkArg((f_dwVerifyFlags & ~DRM_DCP_VERIFY_ENTIRE_DEVCERT) == 0);

    ChkDRMString( f_pdstrDevCert );

    /* verify basic values */
    ChkDR( _DCP_VerifyBasicValues( f_pdstrDevCert, f_pcontextCRYP ) );
    
    if( f_dwVerifyFlags & DRM_DCP_VERIFY_GROUP_CERT )
    {
        /*
        **  Get the Public Key
        */
        ChkDR( DRM_DCP_GetAttribute( f_pdstrDevCert, 
                                    DRM_DEVCERT_DACPUBKEY,
                                    NULL,
                                   &dstrPubkey ) );
        
        ChkDR( _DCP_VerifyCert( f_pdstrDevCert, 
                                f_pcontextCRYP, 
                               &dstrPubkey,
                                DRM_DEVCERT_GROUPCERTDATANODE, 
                                DRM_DEVCERT_GROUPCERTSIGNATURE ) );
    }
    
    if (f_dwVerifyFlags & DRM_DCP_VERIFY_DEVICE_CERT)
    {    
        DRM_DWORD cb = 0;


        /* Get the compatability certificate */ 
        ChkDR(DRM_DCP_GetAttribute(f_pdstrDevCert, 
                                   DRM_DEVCERT_COMPATIBILITYCERT, 
                                   NULL,
                                  &dstrPubkey));
        cb = SIZEOF(PKCERT);
        if (DRM_FAILED(DRM_B64_DecodeW(&dstrPubkey,
                                       &cb, 
                          (DRM_BYTE *) &f_pcontextCRYP->union_cert.pkcert,
                                        0)))
        {
            dr = DRM_E_INVALIDDEVICECERTIFICATE;
            goto ErrorExit;
        }

        /*
        **  Get the Public Key
        */
        ChkDR( DRM_DCP_GetAttribute( f_pdstrDevCert, 
                                     DRM_DEVCERT_GROUPCERTPUBKEY,
                                     NULL,
                                    &dstrPubkey ) );
        cb = SIZEOF(PUBKEY);
        if (DRM_FAILED(DRM_B64_DecodeW(&dstrPubkey, 
                                       &cb, 
                            (DRM_BYTE*)&f_pcontextCRYP->pubKey, 
                                        0)))
        {
            ChkDR(DRM_E_INVALIDDEVICECERTIFICATE);
        }

        /* Verify the rest of the certificate */
        ChkDR( _DCP_VerifyCert( f_pdstrDevCert, 
                                f_pcontextCRYP, 
                               &dstrPubkey,
                                DRM_DEVCERT_DEVICEDATANODE,
                                DRM_DEVCERT_DEVICESIGNATURE) );

    }
    
    if (f_dwVerifyFlags & DRM_DCP_VERIFY_DAC)
    {
        INIT_DRM_STRING(dstrPubkey);
        
        ChkDR(DRM_DCP_GetAttribute(f_pdstrDevCert, 
                                   DRM_DEVCERT_DACROOTPUBKEY, 
                                   NULL,
                                  &dstrPubkey));
    
        ChkDR( _DCP_VerifyCert    (f_pdstrDevCert, 
                                   f_pcontextCRYP, 
                                  &dstrPubkey, 
                                   DRM_DEVCERT_DACDATANODE, 
                                   DRM_DEVCERT_DACSIGNATURE) );      

        ChkDR( _DCP_VerifyCert    (f_pdstrDevCert, 
                                   f_pcontextCRYP, 
                                  &g_dstrMSRootPubKey, 
                                   DRM_DEVCERT_AUTHROOTDATANODE, 
                                   DRM_DEVCERT_AUTHROOTSIGNATURE)); 
    }

ErrorExit:
    return dr;
}

DRM_RESULT DRM_API DRM_DCP_LoadPropertiesCache(
    IN const DRM_CONST_STRING                      *f_pdstrDevCert,
       OUT   DRM_DEVICE_CERTIFICATE_CACHED_VALUES  *f_pcacheDevCert,
    IN       DRM_CRYPTO_CONTEXT                    *f_pcontextCRYP )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_CONST_STRING dstrValue = EMPTY_DRM_STRING;
    DRM_DWORD cb     = 0;
    DRM_LONG  lValue = 0;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_DCP_LoadPropertiesCache", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);

    ChkArg( f_pcacheDevCert != NULL
         && f_pcontextCRYP  != NULL );
    ChkDRMString( f_pdstrDevCert );
    
    /*
    ** First get all the keys and certificates
    */ 

    ChkDR( DRM_DCP_GetAttribute( f_pdstrDevCert, DRM_DEVCERT_DEVICEPUBKEY, NULL, &dstrValue) );
    cb = SIZEOF( PUBKEY );
    if (DRM_FAILED(DRM_B64_DecodeW(&dstrValue, 
                                   &cb, 
                      (DRM_BYTE*)&(f_pcacheDevCert->pubkeyCurrent),
                                    0)))
    {
        ChkDR( DRM_E_INVALIDDEVICECERTIFICATE );
    }
    
    ChkDR( DRM_DCP_GetAttribute( f_pdstrDevCert, DRM_DEVCERT_DEVICEPRIVKEY, NULL, &dstrValue) );
    
    if ( dstrValue.cchString != CCH_BASE64_EQUIV( SIZEOF(PRIVKEY) ) )
    {
        ChkDR( DRM_E_INVALIDDEVICECERTIFICATE );
        goto ErrorExit;
    }

    cb = SIZEOF(PRIVKEY);
    if (DRM_FAILED(DRM_B64_DecodeW(&dstrValue, 
                                   &cb, 
                                   (DRM_BYTE*)f_pcacheDevCert->m_blobDevicePrivkey,
                                    0)))
    {
        ChkDR( DRM_E_INVALIDDEVICECERTIFICATE );
    }    

    /* get the Compatibility cert */
    ChkDR(DRM_DCP_GetAttribute(f_pdstrDevCert, DRM_DEVCERT_COMPATIBILITYCERT, NULL, &dstrValue));
    
    cb = SIZEOF(PKCERT);
    if (DRM_FAILED(DRM_B64_DecodeW(&dstrValue,
                                    &cb, 
                        (DRM_BYTE*)&f_pcacheDevCert->m_BBCompatibilityCert,
                                    0)))
    {
        ChkDR( DRM_E_INVALIDDEVICECERTIFICATE );
    }
    
    /* Get the legacy version */
    if ( DRM_SUCCEEDED( DRM_DCP_GetAttribute( f_pdstrDevCert, 
                                              DRM_DEVCERT_COMPATIBILITYVERSION, 
                                              NULL, 
                                              &dstrValue) ) )
    {
        if( dstrValue.cchString > NO_OF( f_pcacheDevCert->wszLegacyVersion ) - 1 )
        {
            ChkDR( DRM_E_INVALIDDEVICECERTIFICATE );
        }
        ChkDR( OEM_StringCchCopyN( f_pcacheDevCert->wszLegacyVersion, 
                            NO_OF( f_pcacheDevCert->wszLegacyVersion ), 
                                   dstrValue.pwszString, 
                                   dstrValue.cchString ) );
        f_pcacheDevCert->wszLegacyVersion[dstrValue.cchString] = g_wchNull;
    }
    else
    {
        /* There is no compatability certificate version. */
        ChkDR( DRM_E_INVALIDDEVICECERTIFICATE );
    }
    
    /*
    ** Next populate all the feature flags in the cached structure
    */ 

    f_pcacheDevCert->dwFeatureFlags = 0;

    ChkDR(DRM_DCP_GetAttribute(f_pdstrDevCert, DRM_DEVCERT_GROUPSECURITYLEVEL, NULL, &dstrValue));
    ChkDR(wcsntol( dstrValue.pwszString, dstrValue.cchString, (DRM_LONG *) &f_pcacheDevCert->appSec ) );

    ChkDR(DRM_DCP_GetAttribute( f_pdstrDevCert, DRM_DEVCERT_SUBJECTID, NULL, &dstrValue));
    ChkDR(wcsntol(dstrValue.pwszString, dstrValue.cchString, (DRM_LONG *) &f_pcacheDevCert->appcd_subject));

    /* Get the secure clock from the devcert */    
    if ( DRM_SUCCEEDED( DRM_DCP_GetAttribute( f_pdstrDevCert, DRM_DEVCERT_SECURECLOCK, NULL, &dstrValue) ) )
    {
        ChkDR( wcsntol ( dstrValue.pwszString, dstrValue.cchString, &lValue ) );
        if( lValue == DRM_CLOCK_SECURE )
        {
            /* If the device supports secure clock then there must be a secure clock server public key in the cert */
            ChkDR( DRM_DCP_GetAttribute( f_pdstrDevCert, DRM_DEVCERT_SECURECLOCKPUBKEY, NULL, &dstrValue) );
            cb = SIZEOF( PUBKEY );
            if( DRM_FAILED( DRM_B64_DecodeW( &dstrValue, 
                                             &cb, 
                                  (DRM_BYTE*)&(f_pcacheDevCert->pubkeySecureClockServer),
                                              0)))
            {
                ChkDR( DRM_E_INVALIDDEVICECERTIFICATE );
            }                
            f_pcacheDevCert->dwFeatureFlags |= DRM_FEATURE_SECURE_CLOCK;
#if DRM_SUPPORT_ANTIROLLBACK_CLOCK            
            f_pcacheDevCert->dwFeatureFlags |= DRM_FEATURE_ANTI_ROLLBACK_CLOCK;
#endif
        }

#if DRM_SUPPORT_ANTIROLLBACK_CLOCK            
        if( lValue == DRM_CLOCK_ANTIROLLBACK )
        {
            f_pcacheDevCert->dwFeatureFlags |= DRM_FEATURE_ANTI_ROLLBACK_CLOCK;            
        }
#endif        
    }

    /* Get the Metering from the devcert */
    if ( DRM_SUCCEEDED( DRM_DCP_GetAttribute( f_pdstrDevCert, 
                                              DRM_DEVCERT_METERING, 
                                              NULL,
                                             &dstrValue) ) )
    {
        ChkDR( wcsntol ( dstrValue.pwszString, dstrValue.cchString, &lValue ) );
        if( lValue == 1 )
        {
            f_pcacheDevCert->dwFeatureFlags |= DRM_FEATURE_METERING;
        }
    }

ErrorExit:
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_DCP_LoadPropertiesCache", g_pwszLeavingFunction);

    return dr;
}
