/**@@@+++@@@@******************************************************************
**
** 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 <drmrsaex.h>
#include <drmdevcert.h>
#include <drmcert.h>
#include <drmxmlbuilder.h>
#include <drmcertparser.h>
#include <drmbase64.h>


#if DRM_SUPPORT_REVOCATION
/******************************************************************************
** Function :   DRM_WCP_GetCertificateType
** Synopsis :   determine the 'level' of a certificate (CertificateAuthority,
**              BlackBox, etc).
** Arguments :  [f_pszBase]     : base of buffer
**              [f_pdasstrCert] : SUBSTRING pointing to the entire certificate
**              [f_pcerttype]   : variable to receive the type
******************************************************************************/

DRM_RESULT DRM_API DRM_WCP_GetCertificateType(
  IN    const DRM_CHAR             *f_pszBase,
  IN    const DRM_SUBSTRING        *f_pdasstrCert,
  OUT   enum  DRM_CERTIFICATE_TYPE *f_pcerttype)
{
    DRM_RESULT    dr = DRM_SUCCESS;
    DRM_SUBSTRING dasstrData     = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrBody     = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrSecurity = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrManufacturerData    = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrModel    = EMPTY_DRM_SUBSTRING;

    ChkArg(f_pszBase       != NULL
        && f_pdasstrCert   != NULL
        && f_pcerttype     != NULL);

    ChkDR(DRM_XML_GetNodeA(f_pszBase,  
                           f_pdasstrCert, 
                          &g_dastrTagWMDRMCertificate, 
                           NULL, 
                           NULL, 
                           0, 
                           NULL,
                          &dasstrBody));

    /* HWID is one of 2 tags only found in machine certs */

    ChkDR(DRM_XML_GetNodeA(f_pszBase,  
                          &dasstrBody, 
                          &g_dastrTagWMDRMData, 
                           NULL, 
                           NULL, 
                           0, 
                           NULL,
                          &dasstrData));

    dr = DRM_XML_GetNodeA(f_pszBase,  
                          &dasstrData, 
                          &g_dastrTagHardwareID, 
                           NULL, 
                           NULL, 
                           0, 
                          &dasstrSecurity,
                           NULL);

    /* the distinction between machine and other certs is the only one that matters here */

    if (dr == DRM_SUCCESS)
    {
        *f_pcerttype = certtypeMachine;
    }
    else
    {
        DRM_SUBSTRING dasstrDataNodes = EMPTY_DRM_SUBSTRING;
        DRM_SUBSTRING dasstrFeature   = EMPTY_DRM_SUBSTRING;
        DRM_SUBSTRING dasstrOne       = EMPTY_DRM_SUBSTRING;

        /* It's one of the leafmost certificate types; narrow it down */
        /* can't use GetAttribute here, it calls this function */

        ChkDR(DRM_XML_GetNodeA (f_pszBase, &dasstrBody,      &g_dastrTagWMDRMData,         NULL, NULL, 0, NULL, &dasstrDataNodes));
        dr =  DRM_XML_GetNodeA (f_pszBase, &dasstrDataNodes, &g_dastrTagWMDRMCertFeatures, NULL, NULL, 0, NULL, &dasstrDataNodes);

        if (DRM_SUCCEEDED(dr))
        {
            ChkDR(DRM_XML_EnumNextNodeA(f_pszBase, &dasstrDataNodes, 0, &dasstrFeature,               NULL, &dasstrOne, NULL,  NULL));

            if (DRM_UTL_DASSTRStringsEqual    (f_pszBase, &dasstrFeature, &g_dastrFeatureExport))
            {
                *f_pcerttype = certtypeExport;
            }
            else if (DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrFeature, &g_dastrFeatureExportIncl))
            {
                *f_pcerttype = certtypeExportIncl;
            }
            else if (DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrFeature, &g_dastrFeatureSampleProtection))
            {
                *f_pcerttype = certtypeSampleProtection;
            }
            else 
            {
                /* If the cert has a ManufacturerData->ModelNumber tag, it must be a non-machine, device cert.
                 * Every cert MUST have a ManufacturerData node 
                 */
                dr = DRM_XML_GetNodeA(f_pszBase, &dasstrData, &g_dastrTagManufacturerData, NULL, NULL, 0, NULL, &dasstrManufacturerData );
                ChkDR( dr );
                
                dr = DRM_XML_GetNodeA(f_pszBase, &dasstrManufacturerData, &g_dastrTagModelNumber, NULL, NULL, 0, &dasstrModel, NULL);
                if( dr == DRM_SUCCESS )
                {
                    *f_pcerttype = certtypeDevice; 
                }
                else
                {
                    *f_pcerttype = certtypeCertAuthority; 
                    dr = DRM_SUCCESS;
                }
            }
        }
        else
        {
            /* This code is reached when a CRL signing cert is being parsed */
            *f_pcerttype = certtypeCertAuthority; 
            dr = DRM_SUCCESS;
        }
    }

    ErrorExit:
        return dr;
} /* DRM_WCP_GetCertificateType */

/******************************************************************************
** Function :   
** Synopsis :   
** Arguments :  [f_pszBase]     : base of buffer
**              [f_pdasstrCert] : SUBSTRING pointing to the entire certificate
**
******************************************************************************/

DRM_RESULT DRM_API DRM_WCP_GetAttribute(
  IN const DRM_CHAR              *f_pszBase,
  IN const DRM_SUBSTRING         *f_pdasstrCert,
  IN     DRM_WMDRM_CERT_ATTRIBUTE f_attribute,
     OUT DRM_SUBSTRING            f_rgdasstrValue [],
  IN OUT DRM_DWORD               *f_pcEntries)
{
    DRM_RESULT    dr                = DRM_SUCCESS;
    DRM_DWORD     iNode             = 0;
    DRM_DWORD     cNodes            = 0;
    DRM_ANSI_CONST_STRING dastrCert = EMPTY_DRM_STRING;
    DRM_SUBSTRING dasstrData        = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrDataNodes   = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrUsage       = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstr0           = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstr1           = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstr2           = EMPTY_DRM_SUBSTRING;
    enum DRM_CERTIFICATE_TYPE certtype = certtypeUnknown;

    ChkArg(f_pszBase       != NULL
        && f_pdasstrCert   != NULL
        && f_pcEntries     != NULL
        && f_pdasstrCert->m_cch > 0);

    ChkDR(DRM_WCP_GetCertificateType(f_pszBase,
                                     f_pdasstrCert,
                                    &certtype));

    /* only machine certs have a security level attribute */

    if (certtype == certtypeMachine)
    {
        ChkArg(f_attribute  == DRM_WMDRM_CERT_ATTR_SECURITY_LEVEL
            || f_attribute  == DRM_WMDRM_CERT_ATTR_KEY_USAGE
            || f_attribute  == DRM_WMDRM_CERT_ATTR_FEATURES
            || f_attribute  == DRM_WMDRM_CERT_ATTR_SECURITY_VERSION
            || f_attribute  == DRM_WMDRM_CERT_ATTR_PUBLIC_KEY_SELF_RSA
            || f_attribute  == DRM_WMDRM_CERT_ATTR_PUBLIC_KEY_SIGNER_RSA
            || f_attribute  == DRM_WMDRM_CERT_ATTR_DIGEST_VALUE );
    }
    else if (certtype == certtypeDevice)
    {
        ChkArg(f_attribute  == DRM_WMDRM_CERT_ATTR_SECURITY_LEVEL
            || f_attribute  == DRM_WMDRM_CERT_ATTR_KEY_USAGE
            || f_attribute  == DRM_WMDRM_CERT_ATTR_FEATURES
            || f_attribute  == DRM_WMDRM_CERT_ATTR_PUBLIC_KEY_SELF_RSA
            || f_attribute  == DRM_WMDRM_CERT_ATTR_PUBLIC_KEY_SIGNER_RSA
            || f_attribute  == DRM_WMDRM_CERT_ATTR_DIGEST_VALUE );
    }
    else
    {
        ChkArg(f_attribute  == DRM_WMDRM_CERT_ATTR_KEY_USAGE
            || f_attribute  == DRM_WMDRM_CERT_ATTR_FEATURES
            || f_attribute  == DRM_WMDRM_CERT_ATTR_SECURITY_VERSION
            || f_attribute  == DRM_WMDRM_CERT_ATTR_PUBLIC_KEY_SELF_RSA
            || f_attribute  == DRM_WMDRM_CERT_ATTR_PUBLIC_KEY_SIGNER_RSA
            || f_attribute  == DRM_WMDRM_CERT_ATTR_DIGEST_VALUE );
    }

    ChkDR(DRM_XML_GetNodeA(f_pszBase, f_pdasstrCert, &g_dastrTagWMDRMCertificate, NULL, NULL, 0, NULL, &dasstrData));

    /* this attribute is retrieved from the <Signature> node, not from <c:Data> */

    if (f_attribute != DRM_WMDRM_CERT_ATTR_PUBLIC_KEY_SIGNER_RSA)
    {
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrData,   &g_dastrTagWMDRMData,        NULL, NULL, 0, NULL, &dasstrDataNodes));
    }

    switch (f_attribute)
    {
    case DRM_WMDRM_CERT_ATTR_KEY_USAGE:
        /*    
        ** <c:KeyUsage>
        **    <c:SignCertificate/>
        **    <c:SignCRL/>
        **    <c:EncryptKey/>
        ** </c:KeyUsage>
        */

        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrDataNodes, &g_dastrTagWMDRMCertKeyUsage, NULL, NULL, 0, NULL, &dasstrUsage));

        ChkDR(DRM_XML_CountMatchingNodesA(f_pszBase, &dasstrUsage, NULL, NULL, NULL, &cNodes));

        if (f_rgdasstrValue == NULL
        || *f_pcEntries      < cNodes)
        {
            *f_pcEntries = cNodes;
            ChkDR(DRM_E_BUFFERTOOSMALL);
        }
        
        *f_pcEntries = cNodes;

        for (iNode = 0; iNode < cNodes; iNode++)
        {
            DRM_SUBSTRING dasstrTag = EMPTY_DRM_SUBSTRING;

            ChkDR(DRM_XML_EnumNextNodeA(f_pszBase,
                                       &dasstrUsage, 
                                        iNode,
                                        f_rgdasstrValue + iNode, /* strings are in the <Tag> */
                                        NULL,
                                       &dasstrTag,
                                        NULL,
                                        NULL));

        }
        break;
    
    case DRM_WMDRM_CERT_ATTR_SECURITY_LEVEL:
        if (f_rgdasstrValue == NULL
        || *f_pcEntries      < 1)
        {
            *f_pcEntries = 1;
            ChkDR(DRM_E_BUFFERTOOSMALL);
        }

       *f_pcEntries = 1;

       ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrDataNodes, &g_dastrTagWMDRMCertSecurityLevel, NULL, NULL, 0, NULL, f_rgdasstrValue));
        break;

    case DRM_WMDRM_CERT_ATTR_SECURITY_VERSION:
        if (f_rgdasstrValue == NULL
        || *f_pcEntries      < 1)
        {
            *f_pcEntries = 1;
            ChkDR(DRM_E_BUFFERTOOSMALL);
        }

       *f_pcEntries = 1;

        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrDataNodes, &g_dastrTagWMDRMCertSecurityVersion, NULL, NULL, 0, NULL, f_rgdasstrValue));
        break;

    case DRM_WMDRM_CERT_ATTR_FEATURES:
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrDataNodes, &g_dastrTagWMDRMCertFeatures,        NULL, NULL, 0, NULL, &dasstrDataNodes));

        ChkDR(DRM_XML_CountMatchingNodesA(f_pszBase,
                                         &dasstrDataNodes, 
                                          NULL,
                                          NULL,
                                          NULL,
                                         &cNodes));

        if (f_rgdasstrValue == NULL
        || *f_pcEntries      < cNodes)
        {
            *f_pcEntries = cNodes;
            ChkDR(DRM_E_BUFFERTOOSMALL);
        }
        
        *f_pcEntries = cNodes;

        for (iNode = 0; iNode < cNodes; iNode++)
        {
            DRM_SUBSTRING dasstrTag = EMPTY_DRM_SUBSTRING;
            DRM_SUBSTRING dasstrOne = EMPTY_DRM_SUBSTRING;

            ChkDR(DRM_XML_EnumNextNodeA(f_pszBase,
                                       &dasstrDataNodes, 
                                        iNode,
                                       &dasstrTag,
                                        f_rgdasstrValue + iNode,     
                                       &dasstrOne,
                                        NULL,
                                        NULL));

            /* only a string "1" is allowed for the data */

            ChkArg(DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrOne, &g_dastrOne));
        }
        break;

    case DRM_WMDRM_CERT_ATTR_PUBLIC_KEY_SELF_RSA:
        if (f_rgdasstrValue == NULL
        || *f_pcEntries      < 2)
        {
            *f_pcEntries = 2;
            ChkDR(DRM_E_BUFFERTOOSMALL);
        }
        
        *f_pcEntries = 2;

        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrDataNodes, &g_dastrTagWMDRMCertPublicKey, NULL, NULL, 0, NULL, &dasstr0));
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstr0,         &g_dastrTagKeyValue,           NULL, NULL, 0, NULL, &dasstr1));
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstr1,         &g_dastrTagRSAKeyValue,        NULL, NULL, 0, NULL, &dasstr2));
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstr2,         &g_dastrTagExponent,           NULL, NULL, 0, NULL, f_rgdasstrValue));
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstr2,         &g_dastrTagModulus,            NULL, NULL, 0, NULL, f_rgdasstrValue + 1));
        break;

    case DRM_WMDRM_CERT_ATTR_PUBLIC_KEY_SIGNER_RSA:
        if (f_rgdasstrValue == NULL
        || *f_pcEntries      < 2)
        {
            *f_pcEntries = 2;
            ChkDR(DRM_E_BUFFERTOOSMALL);
        }
        
        *f_pcEntries = 2;

        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrData, &g_dastrTagSignature,   NULL, NULL, 0, NULL, &dasstr0));
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstr0,    &g_dastrTagKeyInfo,     NULL, NULL, 0, NULL, &dasstr1));
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstr1,    &g_dastrTagKeyValue,    NULL, NULL, 0, NULL, &dasstr2));
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstr2,    &g_dastrTagRSAKeyValue, NULL, NULL, 0, NULL, &dasstr0));
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstr0,    &g_dastrTagExponent,    NULL, NULL, 0, NULL, f_rgdasstrValue));
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstr0,    &g_dastrTagModulus,     NULL, NULL, 0, NULL, f_rgdasstrValue + 1));
        break;

    case DRM_WMDRM_CERT_ATTR_DIGEST_VALUE:
        if (f_rgdasstrValue == NULL 
        || *f_pcEntries < 1)
        {
            *f_pcEntries = 1;
            ChkDR( DRM_E_BUFFERTOOSMALL);
        }

        *f_pcEntries = 1;

        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrData, &g_dastrTagSignature,   NULL, NULL, 0, NULL, &dasstr0));
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstr0,    &g_dastrTagSignedInfo,  NULL, NULL, 0, NULL, &dasstr1));
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstr1,    &g_dastrTagReference,   NULL, NULL, 0, NULL, &dasstr2));
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstr2,    &g_dastrTagDigestValue, NULL, NULL, 0, NULL, f_rgdasstrValue));
        break;

    default:
        ChkDR(DRM_E_INVALIDARG);
        break;
    } /* end switch f_attribute*/
    
    ErrorExit:
        return dr;
} /* DRM_WCP_GetAttribute */

/******************************************************************************
** Function :   
** Synopsis :   
** Arguments :  
**              
**              
**              
** 
******************************************************************************/

DRM_RESULT DRM_API DRM_WCP_GetHardwareID(
  IN const DRM_CHAR      *f_pszBase,
  IN const DRM_SUBSTRING *f_pdasstrCert,
     OUT   DRM_BYTE       f_rgbHardwareID [SHA_DIGEST_LEN])
{
    DRM_RESULT    dr = DRM_SUCCESS;
    DRM_SUBSTRING dasstrData        = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrDataNodes   = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrHardwareID  = EMPTY_DRM_SUBSTRING;
    DRM_DWORD     cbHardwareID      = SHA_DIGEST_LEN;
    enum DRM_CERTIFICATE_TYPE certtype = certtypeUnknown;

    ChkArg(f_pszBase       != NULL
        && f_pdasstrCert   != NULL
        && f_rgbHardwareID != NULL);

    /* only machine certs have a HardwareID */

    ChkDR(DRM_WCP_GetCertificateType(f_pszBase,
                                     f_pdasstrCert,
                                    &certtype));

    ChkArg(certtype == certtypeMachine);

    ChkDR(DRM_XML_GetNodeA(f_pszBase,  f_pdasstrCert,    &g_dastrTagWMDRMCertificate, NULL, NULL, 0, NULL, &dasstrData));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrData,       &g_dastrTagWMDRMData,        NULL, NULL, 0, NULL, &dasstrDataNodes));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrDataNodes,  &g_dastrTagHardwareID,       NULL, NULL, 0, NULL, &dasstrHardwareID));

    ChkDR(DRM_B64_DecodeA (f_pszBase, &dasstrHardwareID, &cbHardwareID, f_rgbHardwareID, 0));

    ErrorExit:
        return dr;
}


/******************************************************************************
** Function :   DRM_WCP_GetSignatureDigest
** Synopsis :   retrieve the certificate's SHA digest from the signature
** Arguments :  [f_pszBase]     : base of buffer
**              [f_pdasstrCert] : SUBSTRING pointing to the entire certificate
**              [f_rgbDigest]   : buffer to receive the SHA digest
******************************************************************************/

DRM_RESULT DRM_API DRM_WCP_GetSignatureDigest(
  IN const DRM_CHAR                *f_pszBase,
  IN const DRM_SUBSTRING           *f_pdasstrCert,
     OUT   DRM_BYTE                 f_rgbDigest [SHA_DIGEST_LEN])
{
    DRM_RESULT    dr = DRM_SUCCESS;
    DRM_SUBSTRING dasstrData         = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrSignature    = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrSignedInfo   = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrReference    = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrDigestValue  = EMPTY_DRM_SUBSTRING;
    DRM_DWORD     cbDigestValue      = SHA_DIGEST_LEN;

    ChkArg(f_pszBase     != NULL
        && f_pdasstrCert != NULL
        && f_rgbDigest   != NULL);

    ChkDR(DRM_XML_GetNodeA(f_pszBase,  f_pdasstrCert,    &g_dastrTagWMDRMCertificate, NULL, NULL, 0, NULL, &dasstrData));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrData,       &g_dastrTagSignature,        NULL, NULL, 0, NULL, &dasstrSignature));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrSignature,  &g_dastrTagSignedInfo,       NULL, NULL, 0, NULL, &dasstrSignedInfo));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrSignedInfo, &g_dastrTagReference,        NULL, NULL, 0, NULL, &dasstrReference));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrReference,  &g_dastrTagDigestValue,      NULL, NULL, 0, NULL, &dasstrDigestValue));

    ChkDR(DRM_B64_DecodeA (f_pszBase, &dasstrDigestValue, &cbDigestValue, f_rgbDigest, 0));

    ErrorExit:
        return dr;
}


/******************************************************************************
** Function :   
** Synopsis :   
** Arguments :  
**              
**              
**              
** 
******************************************************************************/

DRM_RESULT DRM_API DRM_WCP_GetManufacturerNodes(
IN const DRM_CHAR      *f_pszBase,
IN const DRM_SUBSTRING *f_pdasstrCertificate,
     OUT DRM_SUBSTRING *f_pdasstrNamespace,
     OUT DRM_SUBSTRING  f_rgdastrTag        [],
     OUT DRM_SUBSTRING  f_rgdasstrAttrLabel [],
     OUT DRM_SUBSTRING  f_rgdasstrAttrValue [],
     OUT DRM_SUBSTRING  f_rgdasstrValue     [],
     OUT DRM_DWORD     *f_pcEntries)
{
    DRM_RESULT    dr                = DRM_SUCCESS;
    DRM_DWORD     cNodes            = 0;
    DRM_DWORD     iNode             = 0;
    DRM_ANSI_CONST_STRING dastrCert = EMPTY_DRM_STRING;
    DRM_SUBSTRING dasstrData        = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrDataNodes   = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrNode        = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrEntries     = EMPTY_DRM_SUBSTRING;
    enum DRM_CERTIFICATE_TYPE certtype = certtypeUnknown;

    ChkArg(f_pszBase            != NULL
        && f_pdasstrCertificate != NULL
        && f_rgdasstrValue      != NULL
        && f_pcEntries          != NULL);

    /* only machine certs have ManufacturerData */

    ChkDR(DRM_WCP_GetCertificateType(f_pszBase,
                                     f_pdasstrCertificate,
                                    &certtype));

    ChkDR(DRM_XML_GetNodeA(f_pszBase,  f_pdasstrCertificate, &g_dastrTagWMDRMCertificate, NULL, NULL, 0, NULL,        &dasstrData));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrData,           &g_dastrTagWMDRMData,        NULL, NULL, 0, NULL,        &dasstrDataNodes));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrDataNodes,      &g_dastrTagManufacturerData, NULL, NULL, 0, &dasstrNode, &dasstrEntries));

    if (f_pdasstrNamespace != NULL)
    {
        f_pdasstrNamespace->m_ich = 0;
        f_pdasstrNamespace->m_cch = 0;

        /*
        ** return code ignored because absence of a namespace is OK
        */

        DRM_XML_GetNodeAttributeA(f_pszBase, &dasstrNode, &g_dastrPrefixManufacturer, f_pdasstrNamespace);
    }

    /* count the nodes */
    
    ChkDR(DRM_XML_CountMatchingNodesA(f_pszBase, &dasstrEntries, NULL, NULL, NULL, &cNodes));

    if (*f_pcEntries < cNodes)
    {
        *f_pcEntries = cNodes;
        ChkDR(DRM_E_BUFFERTOOSMALL);
    }
    
    *f_pcEntries = cNodes;

    for (iNode = 0; iNode < cNodes; iNode++)
    {
        ChkDR(DRM_XML_EnumNextNodeA(f_pszBase,
                                    &dasstrEntries, 
                                    iNode,
                                    f_rgdastrTag + iNode,
                                    NULL,
                                  ((f_rgdasstrValue     == NULL) ? NULL : f_rgdasstrValue     + iNode),
                                  ((f_rgdasstrAttrLabel == NULL) ? NULL : f_rgdasstrAttrLabel + iNode),
                                  ((f_rgdasstrAttrValue == NULL) ? NULL : f_rgdasstrAttrValue + iNode)));
    }
    
    ErrorExit:
        return dr;
}

static DRM_RESULT _VerifyCertificateMethods(const DRM_CHAR      *f_pszBase,
                                            const DRM_SUBSTRING *f_pdasstrCert,
                                            const DRM_BOOL       f_fWMDRMNET)
{
    DRM_RESULT    dr = DRM_SUCCESS;
    DRM_SUBSTRING dasstrSignature      = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrReference      = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrSignedInfo     = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrElements       = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrTransforms     = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrDummy          = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrTag            = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrNodeData       = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrAttributeName  = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrAttributeValue = EMPTY_DRM_SUBSTRING;

    /* descend into the <c:Certificate>/<Signature> node */

    ChkDR(DRM_XML_GetNodeA(f_pszBase, f_pdasstrCert,     &g_dastrTagWMDRMCertificate,  NULL, NULL, 0, NULL, &dasstrElements));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrElements,   &g_dastrTagSignature,         NULL, NULL, 0, NULL, &dasstrSignature));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrSignature,  &g_dastrTagSignedInfo,        NULL, NULL, 0, NULL, &dasstrSignedInfo));

    /* verify both the presence and the correct values of signature parameters */

    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrSignedInfo, &g_dastrTagCanonicalization, &g_dastrAttributeAlgorithm, &g_dastrURIC14N,        0, NULL, &dasstrDummy));

    if (f_fWMDRMNET == DRM_DSIG_TYPE_WMDRMNET)
    {
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrSignedInfo, &g_dastrTagSignatureMethod,  &g_dastrAttributeAlgorithm, &g_dastrURIRSASHA1_Old, 0, NULL, &dasstrDummy));
    }
    else
    {
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrSignedInfo, &g_dastrTagSignatureMethod,  &g_dastrAttributeAlgorithm, &g_dastrURIRSASHA1,     0, NULL, &dasstrDummy));
    }

    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrSignedInfo, &g_dastrTagReference,         NULL,                       NULL,                  0, NULL, &dasstrReference));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrReference,  &g_dastrTagDigestMethod,     &g_dastrAttributeAlgorithm, &g_dastrURIDSigSHA1,    0, NULL, &dasstrDummy));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrReference,  &g_dastrTagTransforms,        NULL,                       NULL,                  0, NULL, &dasstrTransforms));
    
    /*
    ** <Transform Algorithm="http://www.microsoft.com/DRM/CERT/v2/Data"/>
    ** <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
    **
    ** must both be present and in the given order
    */

    ChkDR(DRM_XML_EnumNextNodeA(f_pszBase, &dasstrTransforms, 0, &dasstrTag, NULL, &dasstrNodeData, &dasstrAttributeName, &dasstrAttributeValue));

    ChkArg(DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrTag,            &g_dastrTagTransform));
    ChkArg(DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrAttributeName,  &g_dastrAttributeAlgorithm));

    if (f_fWMDRMNET == DRM_DSIG_TYPE_WMDRMNET)
    {
        ChkArg(DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrAttributeValue, &g_dastrURITransformMSCert_Old));
    }
    else
    {
        ChkArg(DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrAttributeValue, &g_dastrURITransformMSCert));
    }
    ChkDR(DRM_XML_EnumNextNodeA(f_pszBase, &dasstrTransforms, 1, &dasstrTag, NULL, &dasstrNodeData, &dasstrAttributeName, &dasstrAttributeValue));

    ChkArg(DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrTag,            &g_dastrTagTransform));
    ChkArg(DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrAttributeName,  &g_dastrAttributeAlgorithm));
    ChkArg(DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrAttributeValue, &g_dastrURITransformC14N));

ErrorExit:
    if (DRM_FAILED(dr))
    {
        dr = DRM_E_VERIFICATION_FAILURE;
    }
    
    return dr;
}


static DRM_RESULT _VerifyCertificateSignature(
    IN  const DRM_CHAR            *f_pszBase,
    IN  const DRM_SUBSTRING       *f_pdasstrCert,
    IN  const DRM_BOOL             f_fWMDRMNET,
    IN  const WMDRMNET_CRL_ENTRY  *f_pRevocationEntries,
    IN        DRM_DWORD            f_cRevocationEntries,
        OUT   DRM_SUBSTRING       *f_pdasstrPubkeySigner)
{
    DRM_RESULT   dr = DRM_SUCCESS;
    DRM_SUBSTRING dasstrElements       = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrSignatureIncl  = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrDataIncl       = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING rgdasstrPubKey [2]   = { 0 };
    DRM_DWORD     cEntries             = NO_OF(rgdasstrPubKey);

    ChkArg( f_pszBase != NULL );
    
    /* cache some nodes to parse further below */

    ChkDR(DRM_XML_GetNodeA(f_pszBase,  f_pdasstrCert,   &g_dastrTagWMDRMCertificate, NULL, NULL, 0,  NULL,                 &dasstrElements));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrElements,  &g_dastrTagSignature,        NULL, NULL, 0, &dasstrSignatureIncl,   NULL));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrElements,  &g_dastrTagWMDRMData,        NULL, NULL, 0, &dasstrDataIncl,        NULL));

    /* get the signer's public key */

    ChkDR(DRM_WCP_GetAttribute(f_pszBase,
                               f_pdasstrCert,
                               DRM_WMDRM_CERT_ATTR_PUBLIC_KEY_SIGNER_RSA,
                               rgdasstrPubKey,
                              &cEntries));

    /* verify that the exponent matches the certificate version */

    ChkBOOL(DRM_UTL_DASSTRStringsEqual(f_pszBase, rgdasstrPubKey, &g_dastrWMDRMCertExponent), DRM_E_VERIFICATION_FAILURE);

    /* pass out the modulus if requested */

    if (f_pdasstrPubkeySigner != NULL)
    {
        *f_pdasstrPubkeySigner = rgdasstrPubKey [1];
    }

    ChkDR(DRM_WCP_VerifyDigitalSignature(f_pszBase, 
                                        &dasstrDataIncl, 
                                        &dasstrSignatureIncl, 
                                         f_fWMDRMNET,
                                         f_pRevocationEntries,
                                         f_cRevocationEntries));
ErrorExit:
    return dr;
}



static DRM_RESULT _VerifyCertificateRequiredTags(const DRM_CHAR      *f_pszBase,
                                                 const DRM_SUBSTRING *f_pdasstrCert)
{
    DRM_RESULT   dr = DRM_SUCCESS;
    DRM_SUBSTRING dasstrNode     = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrData     = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrDataNodes= EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrDummy    = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstrElements = EMPTY_DRM_SUBSTRING;
    DRM_DWORD     iNode          = 0;
    enum DRM_CERTIFICATE_TYPE certtype = certtypeUnknown;
    
    ChkDR(DRM_WCP_GetCertificateType(f_pszBase,
                                     f_pdasstrCert,
                                    &certtype));

    ChkDR  (DRM_XML_GetNodeA          (f_pszBase,  f_pdasstrCert, &g_dastrTagWMDRMCertificate,     NULL, NULL, 0, &dasstrNode, &dasstrElements));

    if (DRM_SUCCEEDED(DRM_XML_GetNodeAttributeA (f_pszBase, &dasstrNode,    &g_dastrAttributeVersionWMDRM,  &dasstrData)))
    {
        /* If there is a c:Version attribute validate that it is 2.0 */
        ChkBOOL(DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrData,    &g_dastrVersionWMDRM),           DRM_E_FAIL);
    }

    if (DRM_SUCCEEDED(DRM_XML_GetNodeAttributeA (f_pszBase, &dasstrNode,    &g_dastrPrefixMicrosoftCert,    &dasstrData)))
    {
        /* If there is a xmlns:c attribute validate that it is what we expect */
        ChkBOOL(DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrData,    &g_dastrURITransformMSCertColl), DRM_E_FAIL);
    }

    ChkDR(DRM_XML_GetNodeA            (f_pszBase, &dasstrElements,  &g_dastrTagWMDRMData,          NULL, NULL, 0, NULL, &dasstrDataNodes));

    /* cert public key node */

    ChkDR  (DRM_XML_GetNodeA          (f_pszBase, &dasstrDataNodes, &g_dastrTagWMDRMCertPublicKey,   NULL, NULL, 0, NULL, &dasstrDummy));
    ChkDR  (DRM_XML_GetNodeA          (f_pszBase, &dasstrDummy,     &g_dastrTagKeyValue,             NULL, NULL, 0, NULL, &dasstrDummy));
    ChkDR  (DRM_XML_GetNodeA          (f_pszBase, &dasstrDummy,     &g_dastrTagRSAKeyValue,          NULL, NULL, 0, NULL, &dasstrDummy));
    ChkDR  (DRM_XML_GetNodeA          (f_pszBase, &dasstrDummy,     &g_dastrTagExponent,             NULL, NULL, 0, NULL, &dasstrData));
    ChkBOOL(DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrData,      &g_dastrWMDRMCertExponent),      DRM_E_BAD_RSA_EXPONENT);
    ChkDR  (DRM_XML_GetNodeA          (f_pszBase, &dasstrDummy,     &g_dastrTagModulus,              NULL, NULL, 0, NULL, &dasstrData));
    ChkBOOL(dasstrData.m_cch > 0, DRM_E_FAIL);

    /* <KeyUsage> */

    ChkDR(DRM_XML_GetNodeA            (f_pszBase, &dasstrDataNodes, &g_dastrTagWMDRMCertKeyUsage,  NULL, NULL, 0, NULL, &dasstrDummy));
    
    /* verify legal values */
    
    for (iNode = 0; ; iNode++)
    {
        dr = DRM_XML_EnumNextNodeA    (f_pszBase, &dasstrDummy, iNode, &dasstrData, &dasstrDummy, NULL, NULL, NULL);
        
        if (dr == DRM_E_XMLNOTFOUND)
        {
            dr = DRM_SUCCESS;
            break;
        }
        else
        {
            ChkDR(dr);
 
            if (certtype == certtypeMachine
            ||  certtype == certtypeExport
            ||  certtype == certtypeExportIncl
            ||  certtype == certtypeDevice
            ||  certtype == certtypeSampleProtection)
            {
                if (! DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrData, &g_dastrKeyUsageEncryptKey))
                {
                    ChkDR(DRM_E_FAIL);
                }
            }
            else
            {
                if (! DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrData, &g_dastrKeyUsageSignCert)
                &&  ! DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrData, &g_dastrKeyUsageSignCRL))
                {
                    ChkDR(DRM_E_FAIL);
                }
            }

        }
    } /* end-for KeyUsage nodes */
    
    /* <SecurityLevel> */

    dr = DRM_XML_GetNodeA(f_pszBase, &dasstrDataNodes, &g_dastrTagWMDRMCertSecurityLevel, NULL, NULL, 0, NULL, &dasstrDummy);

    if (certtype == certtypeMachine
    ||  certtype == certtypeExport
    ||  certtype == certtypeExportIncl
    ||  certtype == certtypeDevice
    ||  certtype == certtypeSampleProtection)
    {
        ChkDR(dr);
    }
    else
    {
        /* TODO:  
        **  We should really be validating that each certificiate in the chain has a sec level <= the next one up in the chain
        **
        ** ChkFAIL(dr == DRM_E_XMLNOTFOUND);
        */

        dr = DRM_SUCCESS;
    }

    /* <SecurityVersion> */

    dr = DRM_XML_GetNodeA(f_pszBase, &dasstrDataNodes, &g_dastrTagWMDRMCertSecurityVersion, NULL, NULL, 0, NULL, &dasstrDummy);

    if (certtype == certtypeMachine
    ||  certtype == certtypeExport
    ||  certtype == certtypeExportIncl)
    {
        /* Enforce that all machine certs have this value */
        ChkDR(dr);    
    }
        
    /* if <ManufacturerData> is present then it must follow the scheme */

    dr =  DRM_XML_GetNodeA(f_pszBase, &dasstrDataNodes, &g_dastrTagManufacturerData, NULL, NULL, 0, NULL, &dasstrNode);
    
    if (dr == DRM_E_XMLNOTFOUND)
    {
        dr = DRM_SUCCESS;
    }
    else
    {
        ChkDR(dr);
    
        ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrNode, &g_dastrTagManufacturerName,      NULL, NULL, 0, NULL, &dasstrDummy));
    }
    
ErrorExit:
    if (DRM_FAILED(dr))
    {
        dr = DRM_E_VERIFICATION_FAILURE;
    }
    
    return dr;
}

/******************************************************************************
** Function :   
** Synopsis :   
** Arguments :  
**              
**              
**              
** 
******************************************************************************/

DRM_RESULT DRM_API DRM_WCP_VerifyCertificate(
    IN const DRM_CHAR            *f_pszBase,
    IN const DRM_SUBSTRING       *f_pdasstrCert,
    IN       DRM_DWORD            f_fVerify,
    IN       DRM_BOOL             f_fWMDRMNET,
    IN const WMDRMNET_CRL_ENTRY  *f_pRevocationEntries,
    IN       DRM_DWORD            f_cRevocationEntries,
       OUT   DRM_SUBSTRING       *f_pdasstrPublicRSA)
{
    DRM_RESULT dr = DRM_SUCCESS;

    ChkArg(f_pdasstrCert           != NULL
        && f_pszBase               != NULL
        && f_pdasstrCert->m_cch    != 0);

    if (f_cRevocationEntries > 0)
    {
        ChkArg(f_pRevocationEntries != NULL);
    }

    ChkArg((f_fVerify &  DRM_WCP_VERIFY_CERT_ALL) != 0);
    ChkArg((f_fVerify & ~DRM_WCP_VERIFY_CERT_ALL) == 0);

    if ((f_fVerify & DRM_WCP_VERIFY_CERT_REQUIRED_TAGS) != 0)
    {
        ChkDR(_VerifyCertificateRequiredTags(f_pszBase, f_pdasstrCert));
    }
 
    if ((f_fVerify & DRM_WCP_VERIFY_CERT_METHODS) != 0)
    {
        ChkDR(_VerifyCertificateMethods(f_pszBase, f_pdasstrCert, f_fWMDRMNET));
    }
            
    if ((f_fVerify & DRM_WCP_VERIFY_CERT_SIGNATURE) != 0)
    {
        ChkDR(_VerifyCertificateSignature(f_pszBase, f_pdasstrCert, f_fWMDRMNET, f_pRevocationEntries, f_cRevocationEntries, f_pdasstrPublicRSA))
    }
 
ErrorExit:
    return dr;
}

static DRM_RESULT _GetPublicKey(const DRM_CHAR *f_pszBase,
                                DRM_SUBSTRING  *f_pdasstrCert,
                                DRM_SUBSTRING  *f_pdasstrPubkey)
{
    DRM_RESULT    dr = DRM_SUCCESS;
    DRM_SUBSTRING dasstrData = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstr0    = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstr1    = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING dasstr2    = EMPTY_DRM_SUBSTRING;

    
    ChkDR(DRM_XML_GetNodeA   (f_pszBase,  f_pdasstrCert, &g_dastrTagWMDRMCertificate,   NULL, NULL, 0, NULL, &dasstrData));
    ChkDR(DRM_XML_GetSubNodeA(f_pszBase, &dasstrData,    &g_dastrTagWMDRMCertPublicKey, NULL, NULL, 0, NULL, &dasstr0,    1));
    ChkDR(DRM_XML_GetNodeA   (f_pszBase, &dasstr0,       &g_dastrTagKeyValue,           NULL, NULL, 0, NULL, &dasstr1));
    ChkDR(DRM_XML_GetNodeA   (f_pszBase, &dasstr1,       &g_dastrTagRSAKeyValue,        NULL, NULL, 0, NULL, &dasstr2));
    ChkDR(DRM_XML_GetNodeA   (f_pszBase, &dasstr2,       &g_dastrTagModulus,            NULL, NULL, 0, NULL,  f_pdasstrPubkey));

ErrorExit:
    return dr;
}


static DRM_RESULT _ExtractPublicKey(DRM_CHAR            *f_pszBase, 
                                    DRM_SUBSTRING       *f_pdasstrKeyInfo, 
                                    DRM_RSA_PUBLIC_KEY **f_pppubkeyRSA)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_RSA_PUBLIC_KEY *ppubkeyRSA = NULL;
    DRM_BYTE            rgbModulus [__CB_DECL(DRM_CB_RSA_PUBLIC_MOD_1024)];
    DRM_DWORD           cbModulus = SIZEOF(rgbModulus);
    DRM_SUBSTRING       dasstrKeyValue    = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING       dasstrRSAKeyValue = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING       dasstrModulus     = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING       dasstrExponent    = EMPTY_DRM_SUBSTRING;

     ChkArg( f_pszBase != NULL );
    
    ChkDR(DRM_XML_GetNodeA(f_pszBase,  f_pdasstrKeyInfo,  &g_dastrTagKeyValue,    NULL, NULL, 0, NULL, &dasstrKeyValue));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrKeyValue,    &g_dastrTagRSAKeyValue, NULL, NULL, 0, NULL, &dasstrRSAKeyValue));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrRSAKeyValue, &g_dastrTagModulus,     NULL, NULL, 0, NULL, &dasstrModulus));
    ChkDR(DRM_XML_GetNodeA(f_pszBase, &dasstrRSAKeyValue, &g_dastrTagExponent,    NULL, NULL, 0, NULL, &dasstrExponent));

    ChkArg(DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrExponent, &g_dastrWMDRMCertExponent));

    ChkDR(DRM_WCP_ReadPubKeyModulus( f_pszBase,
                                    &dasstrModulus,
                                     rgbModulus,
                                    &cbModulus ) );

    ChkDR(OEM_DrmRsaSetPublicKey(DRM_RSA_EXPONENT_VER_3_0_0_0, rgbModulus, cbModulus, &ppubkeyRSA));

    *f_pppubkeyRSA = ppubkeyRSA;

ErrorExit:
    return dr;
}


/******************************************************************************
** Function :   DRM_WCP_VerifyCertificateCollection
** Synopsis :   
** Arguments :  [f_pszBase]              :
**              [f_pdasstrCollection]    :   Secure store context for opened slot
**              [f_pdasstrPubkeyMachin ] :   (Optional - can be NULL)If slot already 
**                                  exists, on return it contains slot size
** 
******************************************************************************/

DRM_RESULT DRM_API DRM_WCP_VerifyCertificateCollection(
    IN const DRM_CHAR              *f_pszBase,
    IN const DRM_SUBSTRING         *f_pdasstrCollection,
    IN const DRM_BOOL               f_fWMDRMNET,
    IN const DRM_ANSI_CONST_STRING *f_pdastrRootPubKey,
    IN const WMDRMNET_CRL_ENTRY    *f_pRevocationEntries,
    IN       DRM_DWORD              f_cRevocationEntries,
       OUT   DRM_SUBSTRING         *f_pdasstrLeafCertificate,
       OUT   DRM_SUBSTRING         *f_pdasstrPubkeyMachine)
{
    DRM_RESULT      dr                   = DRM_SUCCESS;
    DRM_DWORD       iNode                = 0;
    DRM_DWORD       cNodes               = 0;
    DRM_DWORD       cMatch               = 0;
    const DRM_CHAR *pszBase              = NULL;
    DRM_SUBSTRING   dasstrPubkeyCertCurr = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING   dasstrCertCurr       = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING   dasstrData           = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING   dasstrNode           = EMPTY_DRM_SUBSTRING;
    DRM_SUBSTRING   dasstrCollectionVersion = EMPTY_DRM_SUBSTRING;
    DRM_DWORD       dwSecurityLevel = (DRM_DWORD) MAX_UNSIGNED_TYPE( DRM_DWORD ); 

    enum DRM_CERTIFICATE_TYPE certtype = certtypeUnknown;

    struct 
    {
        DRM_SUBSTRING   dasstrCertificate;
        DRM_SUBSTRING   dasstrPubKeyCert;
        DRM_SUBSTRING   dasstrPubKeySigner;
    } rgcertInfo [DRM_CERTIFICATE_COLLECTION_MAX] = { 0 };

    DRM_DWORD   rgSecurityLevels[DRM_CERTIFICATE_COLLECTION_MAX] = { 0 };

    ChkArg(f_pdastrRootPubKey != NULL
        && f_pszBase          != NULL);

    ChkDR(DRM_XML_GetNodeA(f_pszBase,
                           f_pdasstrCollection, 
                          &g_dastrTagCertificateCollection, 
                           NULL, 
                           NULL, 
                           0, 
                          &dasstrNode,
                          &dasstrData));

    /* check for certificate collection c:Version attribute */

    if( DRM_SUCCEEDED( DRM_XML_GetNodeAttributeA (f_pszBase, &dasstrNode, &g_dastrAttributeVersionWMDRM, &dasstrCollectionVersion ) ) )
    {
        ChkBOOL(DRM_UTL_DASSTRStringsEqual(f_pszBase, &dasstrCollectionVersion, &g_dastrVersionWMDRM), DRM_E_FAIL);
    }

    /* enumerate c:Certificate nodes */

    ChkDR(DRM_XML_CountMatchingNodesA(f_pszBase,
                                     &dasstrData,
                                     &g_dastrTagWMDRMCertificate,
                                      NULL,
                                      NULL,
                                     &cNodes));

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

    /* Collect public keys and verify certificates */

    for (iNode = 0; iNode < cNodes; iNode++)
    {
        DRM_SUBSTRING dasstrTag         = EMPTY_DRM_STRING;
        DRM_DWORD     cEntries          = 1;
        
        ChkDR(DRM_XML_EnumNextNodeA(f_pszBase,
                                   &dasstrData,
                                    iNode,
                                   &dasstrTag,
                                   &rgcertInfo[iNode].dasstrCertificate,
                                    NULL,
                                    NULL,
                                    NULL));

        /* there should be nothing but <c:Certificate>s in <CertificateCollection> */
        
        ChkArg(DRM_UTL_DASSTRStringsEqual(f_pszBase,
                                         &dasstrTag,
                                         &g_dastrTagWMDRMCertificate));

        ChkDR(DRM_WCP_VerifyCertificate(f_pszBase,
                                       &rgcertInfo[iNode].dasstrCertificate,
                                        DRM_WCP_VERIFY_CERT_ALL,
                                        f_fWMDRMNET,
                                        f_pRevocationEntries,
                                        f_cRevocationEntries,
                                       &rgcertInfo [iNode].dasstrPubKeySigner));

        ChkDR(_GetPublicKey(f_pszBase,
                           &rgcertInfo[iNode].dasstrCertificate,
                           &rgcertInfo[iNode].dasstrPubKeyCert));
    }

    /* 
    ** now we have the public keys parsed out; hook them up 
    ** first find the certificate signed by the MS root cert
    */
    
    dasstrPubkeyCertCurr.m_ich = 0;
    dasstrPubkeyCertCurr.m_cch = f_pdastrRootPubKey->cchString;
    pszBase       = (DRM_CHAR *) f_pdastrRootPubKey->pszString;
    cMatch  = 0;
    iNode   = 0;

    /* loop until we find a match for dasstrPubkeyCertCurr */
    
    while (iNode < cNodes)
    {
        /* skip entries that have already been matched */
        
        if (rgcertInfo [iNode].dasstrPubKeyCert.m_cch > 0)
        {
            if (DRM_UTL_DASSSTRStringsEqual(pszBase, 
                                           &dasstrPubkeyCertCurr, 
                                            f_pszBase,
                                           &rgcertInfo [iNode].dasstrPubKeySigner))
            {
                /* on the first pass we're comparing to a string constant; subsequently we
                ** are comparing within the Collection
                */
                
                if (pszBase != f_pszBase)
                {
                    pszBase = f_pszBase;
                }

                /* we have a match; cache the new signer's pubkey and
                ** zero out this entry to indicate that it's done
                */

                dasstrPubkeyCertCurr = rgcertInfo[iNode].dasstrPubKeyCert;
                dasstrCertCurr       = rgcertInfo[iNode].dasstrCertificate;
                ZEROMEM(&rgcertInfo [iNode], SIZEOF(rgcertInfo [0]));
                
                /* Ensure signer's security level is not less than cert */
                if( dwSecurityLevel < rgSecurityLevels[iNode] )
                {
                    ChkDR( DRM_E_VERIFICATION_FAILURE );
                }
                else
                {
                    dwSecurityLevel = rgSecurityLevels[iNode];
                }

                /* restart the loop */
                
                iNode  = 0;
                cMatch = 0;
            }
            else
            {
                iNode++;
            }
        }
        else
        {
            cMatch++;
            iNode++;
        }
    }    

    if (cMatch != cNodes)
    {
        ChkDR(DRM_E_VERIFICATION_FAILURE);
    }

    /* optionally pass out the pubkey of the leafmost node */
    
    else
    {
        if (f_pdasstrPubkeyMachine != NULL)
        {
            *f_pdasstrPubkeyMachine = dasstrPubkeyCertCurr;
        }

        if (f_pdasstrLeafCertificate != NULL)
        {
            *f_pdasstrLeafCertificate = dasstrCertCurr;
        }
    }
    
ErrorExit:
    return dr;
}


DRM_RESULT DRM_API DRM_WCP_ReadPubKeyModulus(
    IN                        DRM_CHAR      *f_rgchB64,
    IN                        DRM_SUBSTRING *f_pdasstrB64,
    OUT                       DRM_BYTE      *f_pbMod,
    OUT                       DRM_DWORD     *f_pcbMod)
{
    DRM_RESULT dr = DRM_SUCCESS;

    ChkArg( f_rgchB64 && f_pdasstrB64 && f_pbMod && f_pcbMod );

    ChkDR( DRM_B64_DecodeA( f_rgchB64, f_pdasstrB64, f_pcbMod, f_pbMod, 0 ) );

ErrorExit:
    return dr;
}



DRM_RESULT DRM_API DRM_WCP_VerifyDigitalSignature(
    IN const DRM_CHAR             *f_pszBase,
    IN const DRM_SUBSTRING        *f_pdasstrData,      /* inclusive */
    IN const DRM_SUBSTRING        *f_pdasstrSignature, /* inclusive */
    IN const DRM_BOOL              f_fWMDRMNET,
    IN const WMDRMNET_CRL_ENTRY   *f_pRevocationEntries,
    IN       DRM_DWORD             f_cRevocationEntries)
{
    DRM_RESULT          dr                   = DRM_SUCCESS;
    SHA_CONTEXT         contextSHA           = { 0 };
    DRM_SUBSTRING       dasstrSignedInfo     = EMPTY_DRM_STRING;
    DRM_SUBSTRING       dasstrSignatureInner = EMPTY_DRM_STRING;
    DRM_SUBSTRING       dasstrReference      = EMPTY_DRM_STRING;
    DRM_SUBSTRING       dasstrSignatureValue = EMPTY_DRM_STRING;
    DRM_SUBSTRING       dasstrSignedInfoIncl = EMPTY_DRM_STRING;
    DRM_SUBSTRING       dasstrDigestValue    = EMPTY_DRM_STRING;
    DRM_SUBSTRING       dasstrKeyInfo        = EMPTY_DRM_STRING;
    DRM_BYTE            rgbSHAEncoded    [__CB_DECL(SHA_DIGEST_LEN)];
    DRM_BYTE            rgbSHACalculated [__CB_DECL(SHA_DIGEST_LEN)];
    DRM_BYTE            rgbSignature     [__CB_DECL(CB_SIGNATURE_RSA)];
    DRM_DWORD           cbSHAEncoded         = SIZEOF(rgbSHAEncoded);
    DRM_DWORD           cbSignature          = SIZEOF(rgbSignature);
    DRM_RSA_PUBLIC_KEY *ppubkeyRSA           = NULL;

    ChkArg( f_pszBase          != NULL 
        &&  f_pdasstrData      != NULL
        &&  f_pdasstrSignature != NULL );

    ChkDR( DRM_XML_GetNodeA( f_pszBase, 
                             f_pdasstrSignature,    
                            &g_dastrTagSignature,  
                             NULL, 
                             NULL, 
                             0, 
                             NULL,                  
                            &dasstrSignatureInner ) );
    ChkDR( DRM_XML_GetNodeA( f_pszBase, 
                            &dasstrSignatureInner, 
                            &g_dastrTagSignedInfo, 
                             NULL, 
                             NULL, 
                             0, 
                            &dasstrSignedInfoIncl, 
                            &dasstrSignedInfo ) );

    /* extract and decode the DigestValue */

    ChkDR( DRM_XML_GetNodeA( f_pszBase, 
                            &dasstrSignedInfo,  
                            &g_dastrTagReference,   
                            NULL,  
                            NULL,   
                            0, 
                            NULL, 
                           &dasstrReference ) );
    ChkDR( DRM_XML_GetNodeA( f_pszBase, 
                            &dasstrReference,   
                            &g_dastrTagDigestValue, 
                            NULL,  
                            NULL,   
                            0, 
                            NULL, 
                           &dasstrDigestValue ) );
    ChkDR( DRM_B64_DecodeA( f_pszBase, 
                           &dasstrDigestValue, 
                           &cbSHAEncoded,          
                           rgbSHAEncoded, 
                           0 ) );

    /*
    **  Search the revocation entries for this digest value to check if the certificate is revoked.
    */
    while(f_cRevocationEntries > 0)
    {
        f_cRevocationEntries--;
        if ( MEMCMP( &f_pRevocationEntries[f_cRevocationEntries], 
                      rgbSHAEncoded, 
                      SIZEOF(rgbSHAEncoded) ) == 0 )
        {
            /* matched a revocation entry */
            ChkDR( DRM_E_CERTIFICATE_REVOKED );            
        }
    }

    /* calculate the SHA hash from the Data node  */

    DRM_SHA_Init    ( &contextSHA );
    DRM_SHA_UpdateOffset( (const DRM_BYTE*)f_pszBase, 
                           f_pdasstrData->m_ich, 
                           f_pdasstrData->m_cch, 
                          &contextSHA ); 
    DRM_SHA_Finalize( &contextSHA, rgbSHACalculated );

    /* compare them */

    ChkBOOL( MEMCMP( rgbSHAEncoded, 
                     rgbSHACalculated, 
                     SHA_DIGEST_LEN ) == 0, 
                     DRM_E_INVALID_SIGNATURE );

    /* get the signature value */

    ChkDR( DRM_XML_GetNodeA( f_pszBase, 
                            &dasstrSignatureInner, 
                            &g_dastrTagSignatureValue, 
                             NULL, 
                             NULL,   
                             0, 
                             NULL, 
                            &dasstrSignatureValue ) );
    ChkDR( DRM_XML_GetNodeA( f_pszBase, 
                            &dasstrSignatureInner, 
                            &g_dastrTagKeyInfo,        
                             NULL, 
                             NULL,   
                             0, 
                             NULL, 
                            &dasstrKeyInfo ) );
    
    ChkDR( DRM_B64_DecodeA ( f_pszBase, 
                            &dasstrSignatureValue, 
                            &cbSignature,              
                            rgbSignature, 
                            0 ) );

    ChkDR( _ExtractPublicKey( (DRM_CHAR *) f_pszBase, 
                           (DRM_SUBSTRING *) &dasstrKeyInfo, 
                           &ppubkeyRSA ) );

    if (f_fWMDRMNET == DRM_DSIG_TYPE_WMDRMNET)
    {
        ChkDR( DrmRsaVerify( f_pszBase, 
                             (DRM_SUBSTRING *) f_pdasstrData,
                             ppubkeyRSA,
                             rgbSignature,
                             cbSignature ) );
    }
    else
    {
        ChkDR( DrmRsaVerify( f_pszBase, 
                            &dasstrSignedInfoIncl,
                             ppubkeyRSA,
                             rgbSignature,
                             cbSignature ) );
    }

ErrorExit:
    if (ppubkeyRSA != NULL)
    {
        OEM_DrmRsaReleaseKey( ppubkeyRSA );
    }

    return dr;
}

#endif


