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

#include <drmcommon.h>
#include <drmutilities.h>
#include <drmcrt.h>
#include <drmcontextsizes.h>
#include <drmsecurestore.h>
#include <drmhmac.h>
#include <oemimpl.h>

/*
    A secure store entry in the HDS will look like this:
    4 bytes -- type of entry ( see type enum )
    20 bytes -- hmac of data with password
    n bytes of data
    The decrypted data will contain attributes name/value pairs like so:
    DRM_WORD ( length in bytes of the attribute name )
    n bytes of name
    TOKEN - 12 bytes
    --- Next attribute
*/

static const DRM_HDS_NAMESPACE SEC_STORE_NAMESPACE                 = { TWO_BYTES('s', 'e'),  TWO_BYTES('c', 'u'),  TWO_BYTES('r', 'e'),  TWO_BYTES('s', 't'),  TWO_BYTES('o', 'r'),  TWO_BYTES('e', '\0') };
static const DRM_HDS_NAMESPACE METER_STORE_NAMESPACE               = { TWO_BYTES('m', 'e'),  TWO_BYTES('t', 'e'),  TWO_BYTES('r', 'i'),  TWO_BYTES('n', 'g'),  TWO_BYTES('s', 't'),  TWO_BYTES('o', 'r'),   TWO_BYTES('e', '\0') };
static const DRM_HDS_NAMESPACE DEVICE_REGISTRATION_STORE_NAMESPACE = { TWO_BYTES('d', 'e'),  TWO_BYTES('v', 'i'),  TWO_BYTES('c', 'e'),  TWO_BYTES('r', 'e'),  TWO_BYTES('g', 's'),  TWO_BYTES('t', 'o'),   TWO_BYTES('r', 'e')  };
static const DRM_HDS_NAMESPACE PLAYLIST_BURN_STORE_NAMESPACE       = { TWO_BYTES('p', 'l'),  TWO_BYTES('a', 'y'),  TWO_BYTES('l', 'i'),  TWO_BYTES('s', 't'),  TWO_BYTES('b', 'u'),  TWO_BYTES('r', 'n'),   TWO_BYTES('s', 't'),   TWO_BYTES('r', '\0') };


/* In the slot meta data if the high bit is set it is raw data.  Otherwise it is TOKEN data */
#define DRM_SST_RAW_DATA             0x80000000
#define DRM_SST_SLOT_SIZE_MASK       0x3FFF0000
#define DRM_SST_SLOT_SIZE_VALID_MASK 0x40000000
#define DRM_SST_PREALLOCATE_SIZE     500
#define DRM_SST_SLOT_DATA_MASK       0x7FFFFF00
#define DRM_SST_SLOT_VERSION_MASK    0x0000FF00
#define DRM_SST_SLOT_VERSION         0x00000100

#define DRM_SST_SET_SLOT_SIZE_IN_METADATA( dwMetaData, cbSlotData ) \
        {                                                           \
                dwMetaData &= ~DRM_SST_SLOT_SIZE_MASK;              \
                dwMetaData |= (cbSlotData << 16)                    \
                           |  DRM_SST_SLOT_SIZE_VALID_MASK;         \
        }
#define DRM_SST_SET_SLOT_METADATA( dwMetaData )         \
        {                                                           \
                dwMetaData &= ~DRM_SST_SLOT_DATA_MASK;              \
                dwMetaData |= DRM_SST_SLOT_VERSION                  \
                           |  DRM_SST_SLOT_SIZE_VALID_MASK;         \
        }

#define DRM_SST_GET_SLOT_SIZE_FROM_METADATA( cbSlotData, dwMetaData ) cbSlotData = (dwMetaData & DRM_SST_SLOT_SIZE_MASK) >> 16;           

DRM_VOID _PrepareTokenForWrite( TOKEN *pToken )
{
    DRMASSERT( pToken != NULL );

    switch( pToken->TokenType )
    {
    case TOKEN_LONG:
        FIX_ENDIAN_DWORD( pToken->val.lValue );
        break;
    case TOKEN_DATETIME:
        FIX_ENDIAN_QWORD( pToken->val.u64DateTime );
        break;
    }
    FIX_ENDIAN_DWORD( pToken->TokenType );
}


DRM_VOID _PrepareTokenForRead( TOKEN *pToken )
{
    DRMASSERT( pToken != NULL );

    FIX_ENDIAN_DWORD( pToken->TokenType );
    switch( pToken->TokenType )
    {
    case TOKEN_LONG:
        FIX_ENDIAN_DWORD( pToken->val.lValue );
        break;
    case TOKEN_DATETIME:
        FIX_ENDIAN_QWORD( pToken->val.u64DateTime );
        break;
    }
}

/*******************************************************************
 *          PRIVATE FUNCTION _LoadAttributesIntoCache
 *
 * purpose: scan through an entire slot that has been cached and populate an
 *          array of the data for easy manipulation later
 *
 * assumptions:  the data will be in this format
 * |2 |  cb  | Token | 
 * |cb| Name | Value | 
 ******************************************************************/

static DRM_RESULT _LoadAttributesIntoCache(
    IN const DRM_BYTE        *pbSlotData,
    IN       DRM_DWORD        cbSlotData,
       OUT   CachedAttribute  rgAttributes[DRM_MAX_ATTRIBUTES_PER_SST_KEY],
       OUT   DRM_WORD        *pwNumAttributes)
{
    DRM_RESULT dr    = DRM_SUCCESS;

    DRMASSERT( pwNumAttributes != NULL );

    /* This assert ensures that when we write tokens to disk they are align on boundaries that
       are compatible with the DRM_WCHAR alignment.  If they aren't most processors could have a 
       alignment fault later when we access the strings */
    DRMCASSERT( ( SIZEOF( TOKEN ) % SIZEOF( DRM_WCHAR ) ) == 0 );
    for( *pwNumAttributes = 0; 
         ( *pwNumAttributes <DRM_MAX_ATTRIBUTES_PER_SST_KEY ) && ( cbSlotData > 0 );
         (*pwNumAttributes)++ )
    {
        DRM_WORD cbString = 0;
        
        if( cbSlotData < SIZEOF( DRM_WORD ) )
        {
            /* Absolutely too small */
            dr = DRM_E_SECURESTORE_CORRUPT;
            goto ErrorExit;
        }
        MEMCPY( &cbString, pbSlotData, SIZEOF( cbString ) );
        FIX_ENDIAN_WORD( cbString );
        pbSlotData += __CB_DECL(SIZEOF( DRM_WORD ));
        cbSlotData -= SIZEOF( DRM_WORD );

        if( cbSlotData < cbString + SIZEOF( TOKEN ) )
        {
            /* There are not enough bytes left in the slot to finish this token out */
            dr = DRM_E_SECURESTORE_CORRUPT;
            goto ErrorExit;
        }

        DSTR_FROM_PB( &rgAttributes[*pwNumAttributes].dstrAttribute, pbSlotData, cbString );

        pbSlotData += __CB_DECL(cbString);
        cbSlotData -= cbString;
        
        rgAttributes[*pwNumAttributes].pTokenValue = (TOKEN*)pbSlotData;
        pbSlotData += __CB_DECL(SIZEOF( TOKEN ));
        cbSlotData -= SIZEOF( TOKEN );

        ZEROMEM( &rgAttributes[*pwNumAttributes].TokenDelta, SIZEOF( rgAttributes[*pwNumAttributes].TokenDelta ) );
        rgAttributes[*pwNumAttributes].dwFlags = 0;
    }    

ErrorExit:
    return dr;
}

static DRM_RESULT _VerifySlotHash( 
    IN OUT   DRM_SECSTORE_CONTEXT *pcontextSST,
    IN       DRM_BOOL              fRaw,
    IN const DRM_BYTE              rgbPassword [__CB_DECL(SHA_DIGEST_LEN)] )
{
    DRM_RESULT dr = DRM_SUCCESS;    
    DRM_BYTE    *pbSlotHash     = NULL;
    DRM_BYTE    *pbBuffer       = NULL;
    DRM_BYTE rgbVerifiedHash [__CB_DECL(SHA_DIGEST_LEN)];
    DRM_DWORD    cbBuffer       = 0;
    DRM_DWORD    cbHeader       = 0;
    HMAC_CONTEXT oHmacContext;

    cbHeader = ( pcontextSST->dwSlotVersion == 0 )
             ? DRM_SST_SLOT_V0_HEADER_SIZE
             : DRM_SST_SLOT_HEADER_SIZE;
    pbBuffer = pcontextSST->rgbSlotData + __CB_DECL( cbHeader ); 
    /* If it is a raw slot the min-header has been loaded.
       Other wise the whole slot has been cached already */
    ChkDR( DRM_HMAC_Init( &oHmacContext, rgbPassword, SHA_DIGEST_LEN ) );

    if( fRaw )
    {
        DRM_DWORD cbRead = 0;
        DRM_DWORD cbSlot = pcontextSST->cbSlotData - cbHeader;
            
        cbBuffer = SIZEOF( pcontextSST->rgbSlotData ) - cbHeader;
        while( cbSlot > 0 )
        {
            DRM_DWORD cbToRead = min( cbBuffer, cbSlot );

            ChkDR( DRM_HDS_SlotRead( &pcontextSST->oSlotContext, cbToRead, pbBuffer, &cbRead ) );    
            if( cbToRead != cbRead )
            {
                dr = DRM_E_FILEREADERROR;
                goto ErrorExit;
            }
            ChkDR( DRM_HMAC_Update( &oHmacContext, pbBuffer, cbRead ) );
            cbSlot -= cbRead;
        }
    }
    else
    {
        cbBuffer = pcontextSST->cbSlotData  - cbHeader;
        ChkDR( DRM_HMAC_Update( &oHmacContext, pbBuffer, cbBuffer ) );
    }

    ChkDR( DRM_HMAC_Finalize( &oHmacContext, rgbVerifiedHash, SHA_DIGEST_LEN ) );

    pbSlotHash = ( pcontextSST->dwSlotVersion == 0 )
               ? ( pcontextSST->rgbSlotData + __CB_DECL(DRM_SST_SLOT_V0_HASH_OFFSET) )
               : ( pcontextSST->rgbSlotData + __CB_DECL(DRM_SST_SLOT_HASH_OFFSET) );
    if( 0 != MEMCMP( pbSlotHash, rgbVerifiedHash, SHA_DIGEST_LEN ) )
    {
        dr = DRM_E_INVALID_SECURESTORE_PASSWORD;
        goto ErrorExit;
    }

ErrorExit:
    return dr;
}


static DRM_RESULT _InitSlot(
    IN OUT   DRM_SECSTORE_CONTEXT  *pcontextSST,
    IN       DRM_BOOL               fRaw,
    IN       eDRM_SECURE_STORE_TYPE eType,
    IN const DRM_BYTE               rgbPassword[__CB_DECL(SHA_DIGEST_LEN)])
{
    DRM_DWORD    dwSlotMetaData = eType;
    DRM_RESULT   dr        = DRM_SUCCESS;
    DRM_DWORD    cbWritten = 0;
    DRM_DWORD    cbToWrite = 0;
    HMAC_CONTEXT hmac;

    if ( fRaw )
    {
        dwSlotMetaData |= DRM_SST_RAW_DATA;
    }

    /*  Set the slot version    */
    pcontextSST->dwSlotVersion = DRM_SST_SLOT_VERSION;
    
    /* Secure store section was just created.  Add the Initial hash */
    ChkDR(DRM_HMAC_Init(&hmac, rgbPassword, SHA_DIGEST_LEN));
    ChkDR(DRM_HMAC_Finalize(&hmac, pcontextSST->rgbSlotData + __CB_DECL( DRM_SST_SLOT_HASH_OFFSET), SHA_DIGEST_LEN));

    DRM_SST_SET_SLOT_METADATA(dwSlotMetaData);
    DWORD_TO_BYTES(pcontextSST->rgbSlotData, dwSlotMetaData);
    DWORD_TO_BYTES(pcontextSST->rgbSlotData + __CB_DECL( SIZEOF( dwSlotMetaData)), pcontextSST->cbSlotData);

    cbToWrite = SIZEOF(dwSlotMetaData)
              + SIZEOF(pcontextSST->cbSlotData)
              + SHA_DIGEST_LEN;

    ChkDR(DRM_HDS_SlotSeek(&pcontextSST->oSlotContext, 0, eDRM_HDS_SEEKSET,NULL));
    ChkDR(DRM_HDS_SlotWrite(&pcontextSST->oSlotContext, cbToWrite, pcontextSST->rgbSlotData, &cbWritten));
    if ( cbWritten != cbToWrite )
    {
        dr = DRM_E_FILEWRITEERROR;
        goto ErrorExit;
    }
#if !_DATASTORE_WRITE_THRU
    ChkDR( DRM_HDS_CommitNamespace(&pcontextSST->oNsContext) );
#endif

ErrorExit:
    return dr;
}


static DRM_RESULT _LoadSlot(
    IN OUT   DRM_SECSTORE_CONTEXT  *pcontextSST,
    IN       DRM_BOOL               fRaw,
    IN       eDRM_SECURE_STORE_TYPE eType,
    IN const DRM_BYTE               rgbPassword[__CB_DECL(SHA_DIGEST_LEN)])
{
    DRM_RESULT dr        = DRM_SUCCESS;
    DRM_DWORD  cbRead    = 0;
    DRM_DWORD  cbToRead  = 0;
    DRM_DWORD  dwSlotMetaData = 0;

    /* A secure store slot is never this small.  Somethings wrong */
    ChkBOOL(pcontextSST->cbSlot >= DRM_SST_SLOT_V0_HEADER_SIZE, DRM_E_SECURESTORE_CORRUPT);
   
    if ( fRaw )
    {
        cbToRead = DRM_SST_SLOT_V0_HEADER_SIZE;
    }        
    else
    {
        ChkBOOL((pcontextSST->cbSlot <= SIZEOF(pcontextSST->rgbSlotData)), DRM_E_SECURESTORE_FULL);
        cbToRead = pcontextSST->cbSlot;
    }
    
    ChkDR( DRM_HDS_SlotRead( &pcontextSST->oSlotContext, cbToRead, pcontextSST->rgbSlotData, &cbRead ) );
    ChkBOOL(cbToRead == cbRead, DRM_E_FILEREADERROR);
    
    /*
    **  First check the version number
    */
    BYTES_TO_DWORD( dwSlotMetaData, pcontextSST->rgbSlotData );
    pcontextSST->dwSlotVersion = dwSlotMetaData & DRM_SST_SLOT_VERSION_MASK;
    if ( pcontextSST->dwSlotVersion == 0 )
    {            
        if ( (DRM_SST_SLOT_SIZE_VALID_MASK & dwSlotMetaData) != 0 )
        {
            DRM_SST_GET_SLOT_SIZE_FROM_METADATA(pcontextSST->cbSlotData, dwSlotMetaData);
        }
        dwSlotMetaData &= ~DRM_SST_SLOT_SIZE_MASK;  
    }
    else 
    {  
        if ( fRaw )
        {
            /*
            **  We need to read one more DWORD. This DWORD is just the last
            **  DWORD of the hash. We haven't read it yet because we first 
            **  read just DRM_SST_SLOT_V0_HEADER_SIZE amount of data from
            **  the beginning of the slot.
            */
            ChkDR( DRM_HDS_SlotRead( &pcontextSST->oSlotContext, 
                                     DRM_SST_SLOT_SIZEDATA_SIZE, 
                                     pcontextSST->rgbSlotData + __CB_DECL( DRM_SST_SLOT_V0_HEADER_SIZE ), 
                                     &cbRead ) );
            ChkBOOL(cbRead == DRM_SST_SLOT_SIZEDATA_SIZE, DRM_E_FILEREADERROR);
        }
        BYTES_TO_DWORD( pcontextSST->cbSlotData, 
                        pcontextSST->rgbSlotData + __CB_DECL( DRM_SST_SLOT_METADATA_SIZE ) );            
    }
    
    if ( (DRM_SST_SLOT_SIZE_VALID_MASK & dwSlotMetaData) == 0 )
    {            
        pcontextSST->cbSlotData = pcontextSST->cbSlot;
    }  
    else
    {
        dwSlotMetaData &= ~((DRM_DWORD)DRM_SST_SLOT_SIZE_VALID_MASK);
    }
    
    dwSlotMetaData &= ~((DRM_DWORD)DRM_SST_SLOT_VERSION_MASK);
    
    ChkBOOL(pcontextSST->cbSlotData <= pcontextSST->cbSlot, DRM_E_SECURESTORE_CORRUPT);
    if ( fRaw )
    {
        ChkBOOL((dwSlotMetaData == (((DRM_DWORD)eType)|((DRM_DWORD)DRM_SST_RAW_DATA))), DRM_E_SECURESTORE_CORRUPT);
    }
    else
    {
        ChkBOOL((dwSlotMetaData == (DRM_DWORD)eType), DRM_E_SECURESTORE_CORRUPT);
    }       
    
    dr = _VerifySlotHash(pcontextSST, fRaw, rgbPassword);

ErrorExit:

    return dr;
}
static DRM_RESULT _OpenAndVerifySlot( 
    IN OUT DRM_SECSTORE_CONTEXT    *pcontextSST,
    IN     DRM_HDS_CONTEXT         *pcontextHDS,
    const  DRM_BYTE                 rgbPassword [__CB_DECL(SHA_DIGEST_LEN)],
    IN     eDRM_SECURE_STORE_TYPE   eType,
    IN     DRM_BOOL                 fRaw,
    IN     DRM_BOOL                 fCreate )
{
    DRM_RESULT dr              = DRM_SUCCESS;
    DRM_BOOL   fCloseNamespace = FALSE,
               fCloseSlot      = FALSE,
               fCreated        = FALSE;
    DRM_DWORD  dwSlotMetaData  = eType;
    DRM_DWORD   cbRemaining     = 0;
    DRM_DWORD   cbToRead        = 0;
    DRM_DWORD   cbRead          = 0;

    if ( fCreate )
    {
        ChkDR( DRM_HDS_OpenNamespace( pcontextHDS, 
                                      pcontextSST->pNamespaceId, 
                                      eDRM_HDS_CREATE_IF_NEW | eDRM_HDS_LOCKWAIT,
                                      DRM_SECURE_STORE_NUM_CHILD_NODES,
                                      &pcontextSST->oNsContext ) );
    }
    else
    {
        dr = DRM_HDS_OpenNamespace( pcontextHDS, 
                                    pcontextSST->pNamespaceId, 
                                    eDRM_HDS_OPEN_EXISTING | eDRM_HDS_LOCKWAIT,
                                    DRM_SECURE_STORE_NUM_CHILD_NODES,
                                    &pcontextSST->oNsContext );
        if ( dr == DRM_E_HDSNAMESPACENOTFOUND )
        {
            /*
            **  TODO: Why are we changing the errorcode here? What error
            **  code are callers expecting? Need to revisit this.
            */
            dr = DRM_E_FILENOTFOUND;
        }
        ChkDR(dr);
    }
    fCloseNamespace = TRUE;

    if( fRaw )
    {
        dwSlotMetaData |= DRM_SST_RAW_DATA;
    }

    dr = DRM_HDS_OpenSlot (&pcontextSST->oNsContext, 
                           eDRM_HDS_OPEN_EXISTING | eDRM_HDS_LOCKEXCLUSIVE | eDRM_HDS_LOCKWAIT, 
                           &pcontextSST->rgbKey1, 
                           &pcontextSST->rgbKey2,
                            NULL,
                           &pcontextSST->cbSlot,
                           &pcontextSST->oSlotContext);
    if (dr == DRM_E_HDSSLOTNOTFOUND )
    {
        if( fCreate )
        {
            pcontextSST->cbSlotData = DRM_SST_SLOT_HEADER_SIZE;
            pcontextSST->cbSlot     = DRM_SST_SLOT_HEADER_SIZE 
                                    + DRM_SST_PREALLOCATE_SIZE;
            dr = DRM_HDS_OpenSlot (&pcontextSST->oNsContext, 
                                   eDRM_HDS_CREATE_IF_NEW | eDRM_HDS_LOCKEXCLUSIVE | eDRM_HDS_LOCKWAIT, 
                                   &pcontextSST->rgbKey1, 
                                   &pcontextSST->rgbKey2,
                                    NULL,
                                   &pcontextSST->cbSlot,            
                                   &pcontextSST->oSlotContext);
            fCreated = TRUE;
        }
        else
        {
            dr = DRM_E_FILENOTFOUND;
        }
    }
    ChkDR( dr );    
    fCloseSlot = TRUE;

    /* The slot is open.*/
    if( fCreated )
    {
        dr = _InitSlot(pcontextSST, fRaw, eType, rgbPassword);
    }
    else
    {           
        dr = _LoadSlot(pcontextSST, fRaw, eType, rgbPassword);
    }
    pcontextSST->eType = eType;

ErrorExit:
    if( DRM_FAILED( dr ) )
    {
        if( fCloseSlot )
        {
            DRM_HDS_CloseSlot( &pcontextSST->oSlotContext );
        }
        if( fCloseNamespace )
        {
            DRM_HDS_CloseNamespace( &pcontextSST->oNsContext );
        }
    }
    return dr;
}

DRM_RESULT DRM_API DRM_SST_OpenKeyTokens(
    IN OUT   DRM_SECSTORE_CONTEXT   *pcontextSST,
    IN const DRM_ID                 *pKey1,
    IN const DRM_ID                 *pKey2,
    IN const DRM_BYTE                rgbPassword [__CB_DECL(SHA_DIGEST_LEN)],
    IN       DRM_DWORD               dwFlags,
    IN       eDRM_SECURE_STORE_TYPE  eType,
    IN       DRM_HDS_CONTEXT        *pcontextHDS)
{
    DRM_RESULT                  dr        = DRM_SUCCESS;
    DRM_DWORD                   cbRead    = 0;    
    DRM_DWORD                   cbHeader  = 0;
    DRM_BOOL                    fClose    = FALSE;

    DRM_PROFILING_ENTER_SCOPE(L"DRM_SST_OpenKeyTokens", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);
        
    ChkArg( pcontextSST != NULL
         && pKey1       != NULL
         && rgbPassword != NULL
         && pcontextHDS != NULL );

    ZEROMEM( pcontextSST, SIZEOF( *pcontextSST ) );

    /* Check for supported flags */
    if ( ( dwFlags & ~(DRM_SECURE_STORE_CREATE_IF_NOT_EXISTS) ) ||
        !( eType == SECURE_STORE_LICENSE_DATA    ||
           eType == SECURE_STORE_GLOBAL_DATA     ||          
           eType == SECURE_STORE_METERING_DATA   ||
           eType == SECURE_STORE_PLAYLIST_BURNING_DATA ) )
    {
        dr = DRM_E_INVALIDARG;
        goto ErrorExit;
    }
    
    switch( eType )
    {
    case SECURE_STORE_METERING_DATA:
        pcontextSST->pNamespaceId = &METER_STORE_NAMESPACE;
        break;
    case SECURE_STORE_PLAYLIST_BURNING_DATA:
        pcontextSST->pNamespaceId = &PLAYLIST_BURN_STORE_NAMESPACE;
        break;
    case SECURE_STORE_GLOBAL_DATA: /* Fall through */
    case SECURE_STORE_LICENSE_DATA:
        pcontextSST->pNamespaceId = &SEC_STORE_NAMESPACE;
        break;
    }

    /* Copy the IDs so we can open the right slot later */
    MEMCPY( &pcontextSST->rgbKey1, pKey1, SIZEOF( pcontextSST->rgbKey1 ) );
    if( pKey2 )
    {
        MEMCPY( &pcontextSST->rgbKey2, pKey2, SIZEOF( pcontextSST->rgbKey2 ) );
    }
    else
    {
        ZEROMEM( &pcontextSST->rgbKey2, SIZEOF( pcontextSST->rgbKey2 ) );
    }
    
    ChkDR( _OpenAndVerifySlot( pcontextSST, pcontextHDS, rgbPassword, eType, FALSE, dwFlags & DRM_SECURE_STORE_CREATE_IF_NOT_EXISTS ) );
    fClose = TRUE;

    MEMCPY( pcontextSST->rgbPassword, rgbPassword, SHA_DIGEST_LEN );

    /* The data has been read and verified.  Run through the data and create a map of the keys */
    cbHeader = ( pcontextSST->dwSlotVersion == 0) 
             ? DRM_SST_SLOT_V0_HEADER_SIZE
             : DRM_SST_SLOT_HEADER_SIZE;
    ChkDR( _LoadAttributesIntoCache( pcontextSST->rgbSlotData + __CB_DECL( cbHeader ), 
                                     pcontextSST->cbSlotData  - cbHeader,
                                     pcontextSST->rgAttributes,
                                    &pcontextSST->wNumAttributes ) );

    pcontextSST->wNumOriginalAttributes = pcontextSST->wNumAttributes;
    pcontextSST->fInited                = TRUE;
    dr                                  = DRM_SUCCESS;

ErrorExit:
    if (fClose)
    {
        DRM_HDS_CloseSlot(&pcontextSST->oSlotContext);
        DRM_HDS_CloseNamespace(&pcontextSST->oNsContext);
    }

    DRM_PROFILING_LEAVE_SCOPE(L"DRM_SST_OpenKeyTokens", g_pwszLeavingFunction);
        
    return ( dr );
}

/******************************************************************************
** 
** Function :   DRM_SST_OpenAndLockSlot
** 
** Synopsis :   Opens and locks the appropriate slot
** 
** Arguments :  f_pcontextHDS   : 
**              f_eType         :   Type of secure store
**              f_pKey1         :   Key1 buffer
**              f_pKey2         :   Key2 buffer
**              f_rgbPassword   :   Secure store password
**              f_dwFlags       :   Only allowed flag for now is 
**                                  DRM_SECURE_STORE_CREATE_IF_NOT_EXIST 
**              f_pcontextSST   :   Secure store context for opened slot
**              f_pcbData       :   (Optional - can be NULL)If slot already 
**                                  exists, on return it contains slot size
** 
** Returns :    
** 
** Notes :      
** 
******************************************************************************/
DRM_RESULT DRM_API DRM_SST_OpenAndLockSlot(
    IN          DRM_HDS_CONTEXT        *f_pcontextHDS,
    IN          eDRM_SECURE_STORE_TYPE  f_eType,
    IN  const   DRM_ID                 *f_pKey1,
    IN  const   DRM_ID                 *f_pKey2,
    IN  const   DRM_BYTE                f_rgbPassword[__CB_DECL(SHA_DIGEST_LEN)],
    IN          DRM_DWORD               f_dwFlags,
        OUT     DRM_SECSTORE_CONTEXT   *f_pcontextSST,
        OUT     DRM_DWORD              *f_pcbData )
{
    DRM_RESULT  dr  = DRM_SUCCESS;

    /*
    **  Check input
    */
    ChkArg( (f_dwFlags & ~DRM_SECURE_STORE_CREATE_IF_NOT_EXISTS) == 0 );
    ChkArg( f_pcontextSST != NULL
         && f_pcontextHDS != NULL
         && f_pKey1       != NULL
         && f_rgbPassword != NULL );
    
    /*
    **  Clear the output flag in secstore
    */
    f_pcontextSST->fLocked = FALSE;
    
    /*
    **  Check for supported flags
    */
    if (  f_eType != SECURE_STORE_LICENSE_DATA             
       && f_eType != SECURE_STORE_GLOBAL_DATA              
       && f_eType != SECURE_STORE_REVOCATION_DATA          
       && f_eType != SECURE_STORE_METERING_DATA            
       && f_eType != SECURE_STORE_DEVICE_REGISTRATION_DATA 
       && f_eType != SECURE_STORE_PLAYLIST_BURNING_DATA
       && f_eType != SECURE_STORE_CACHED_CERTIFICATE_DATA )
    {
        dr = DRM_E_INVALIDARG;
        goto ErrorExit;
    }

    /*
    **  Initialize the secstore context
    */
    ZEROMEM( f_pcontextSST, SIZEOF( *f_pcontextSST ) );
    switch( f_eType )
    {
    case SECURE_STORE_METERING_DATA:
        f_pcontextSST->pNamespaceId = &METER_STORE_NAMESPACE;
        break;
    case SECURE_STORE_PLAYLIST_BURNING_DATA:
        f_pcontextSST->pNamespaceId = &PLAYLIST_BURN_STORE_NAMESPACE;
        break;
    case SECURE_STORE_DEVICE_REGISTRATION_DATA:
        f_pcontextSST->pNamespaceId = &DEVICE_REGISTRATION_STORE_NAMESPACE;
        break;
    case SECURE_STORE_GLOBAL_DATA: /* Fall through */
    case SECURE_STORE_REVOCATION_DATA:
    case SECURE_STORE_LICENSE_DATA:
    case SECURE_STORE_CACHED_CERTIFICATE_DATA:
        f_pcontextSST->pNamespaceId = &SEC_STORE_NAMESPACE;
        break;
    }
        
    /*
    **  Copy the IDs so we can open the right slot later
    */
    MEMCPY( &f_pcontextSST->rgbKey1, f_pKey1, SIZEOF( f_pcontextSST->rgbKey1 ) );
    if( f_pKey2 != NULL )
    {
        MEMCPY( &f_pcontextSST->rgbKey2, f_pKey2, SIZEOF( f_pcontextSST->rgbKey2 ) );
    }
    else
    {
        ZEROMEM(&f_pcontextSST->rgbKey2, SIZEOF( f_pcontextSST->rgbKey2 ) );
    }

    /*
    **  Open and lock slot
    */
    ChkDR( _OpenAndVerifySlot( f_pcontextSST, 
                               f_pcontextHDS, 
                               f_rgbPassword, 
                               f_eType, 
                               TRUE, 
                               f_dwFlags & DRM_SECURE_STORE_CREATE_IF_NOT_EXISTS ) );

    /*
    **  Save the securestore password
    */
    MEMCPY( f_pcontextSST->rgbPassword, 
            f_rgbPassword, 
            SIZEOF(f_pcontextSST->rgbPassword) );

    /*
    **  Set the slot size for the caller
    */
    if (f_pcbData != NULL)
    {
        *f_pcbData = ( f_pcontextSST->dwSlotVersion == 0 )
                   ? f_pcontextSST->cbSlotData - DRM_SST_SLOT_V0_HEADER_SIZE
                   : f_pcontextSST->cbSlotData - DRM_SST_SLOT_HEADER_SIZE; 
    }   
    
    /*
    **  Secstore context now holds the lock. Set appropriate flag
    */
    f_pcontextSST->fLocked = TRUE;
    
ErrorExit:
    return dr;
}
    

/******************************************************************************
** 
** Function :   DRM_SST_GetLockedData
** 
** Synopsis :   Gets data from the slot if this thread holds the lock to the slot
** 
** Arguments :  f_pcontextSST   : Secure store context
**              f_pbData        : Buffer for slot data
**              f_pcbData       : Buffer size; if slot is bigger, on return, it
**                                would contain the slot size
** 
** Returns :    DRM_E_SECURESTORE_LOCKNOTOBTAINED if  DRM_SST_OpenAndLockSlot has
**              not been successfully called prior to this function
** 
** Notes :      
** 
******************************************************************************/
DRM_RESULT DRM_API DRM_SST_GetLockedData(
    IN       DRM_SECSTORE_CONTEXT   *f_pcontextSST,
       OUT   DRM_BYTE               *f_pbData,
       OUT   DRM_DWORD              *f_pcbData )
{
    DRM_RESULT  dr          = DRM_SUCCESS;
    DRM_DWORD   cbRemaining = 0;
    DRM_DWORD   cbHeader    = 0;

    ChkArg( f_pcontextSST != NULL
         && f_pcbData     != NULL );
    
    /*
    **  Check if the caller holds the lock for the secure store
    */
    if (!f_pcontextSST->fLocked)
    {
        ChkDR(DRM_E_SECURESTORE_LOCKNOTOBTAINED);
    }
    
    /*
    **  Check size of input buffer
    */
    cbHeader = ( f_pcontextSST->dwSlotVersion == 0 )
             ? DRM_SST_SLOT_V0_HEADER_SIZE
             : DRM_SST_SLOT_HEADER_SIZE;
    cbRemaining = f_pcontextSST->cbSlotData - cbHeader;    
    
    if( f_pbData == NULL || *f_pcbData < cbRemaining )
    {
        *f_pcbData = cbRemaining;
        dr = DRM_E_BUFFERTOOSMALL;
        goto ErrorExit;
    }
    *f_pcbData = cbRemaining;
    
    /*
    **  Get the data
    */
    ChkDR( DRM_HDS_SlotSeek( &f_pcontextSST->oSlotContext, 
                             cbHeader, 
                             eDRM_HDS_SEEKSET, NULL ) );
    ChkDR( DRM_HDS_SlotRead( &f_pcontextSST->oSlotContext, 
                             cbRemaining, 
                             f_pbData, 
                             f_pcbData ) );

ErrorExit:
    return dr;
}

/******************************************************************************
** 
** Function :   DRM_SST_SetLockedData
** 
** Synopsis :   Sets data into the slot if this thread holds the lock to the slot
** 
** Arguments :  f_pcontextSST   : Secure store context
**              f_cbData        : Buffer size; 
**              f_pbData        : Data Buffer
** 
** Returns :    DRM_E_SECURESTORE_LOCKNOTOBTAINED if  DRM_SST_OpenAndLockSlot has
**              not been successfully called prior to this function
**
** Notes :
** 
******************************************************************************/
DRM_RESULT DRM_API DRM_SST_SetLockedData(
    IN          DRM_SECSTORE_CONTEXT   *f_pcontextSST,
    IN          DRM_DWORD               f_cbData,
        OUT     DRM_BYTE               *f_pbData )
{
    DRM_RESULT      dr              = DRM_SUCCESS;
    DRM_DWORD       cbWritten       = 0;
    DRM_DWORD       dwSlotMetaData  = 0;
    HMAC_CONTEXT    contextHMAC;

    ChkArg( f_pcontextSST != NULL
         && f_pbData      != NULL );
    
    /*
    **  Check if the caller holds the lock for the secure store
    */
    if (!f_pcontextSST->fLocked)
    {
        ChkDR(DRM_E_SECURESTORE_LOCKNOTOBTAINED);
    }    
    

    f_pcontextSST->cbSlotData = DRM_SST_SLOT_HEADER_SIZE + f_cbData;
    ChkDR( DRM_HDS_SlotResize( &f_pcontextSST->oSlotContext, 
                               f_pcontextSST->cbSlotData ) );
    /*
    **  If this was a v0 slot, update it to current version
    */
    if ( f_pcontextSST->dwSlotVersion == 0 )
    {
        /* Clear the old format size fields in the slot meta data */
        BYTES_TO_DWORD( dwSlotMetaData, f_pcontextSST->rgbSlotData );
        DRM_SST_SET_SLOT_METADATA( dwSlotMetaData );
        DWORD_TO_BYTES( f_pcontextSST->rgbSlotData, dwSlotMetaData );
    }
     
    /*
    **  Obtain HMAC of data to be written to secstore
    */
    ChkDR( DRM_HMAC_Init( &contextHMAC, 
                          f_pcontextSST->rgbPassword, 
                          SHA_DIGEST_LEN ) );
    ChkDR( DRM_HMAC_Update( &contextHMAC, f_pbData, f_cbData ) );
    ChkDR( DRM_HMAC_Finalize( &contextHMAC, 
                              f_pcontextSST->rgbSlotData + __CB_DECL( DRM_SST_SLOT_HASH_OFFSET), 
                              SHA_DIGEST_LEN ) );

    /*
    **  Find the right slot
    */
    ChkDR( DRM_HDS_SlotSeek( &f_pcontextSST->oSlotContext, 
                             0, 
                             eDRM_HDS_SEEKSET,
                             NULL ) );

    /* Update the size in the slot meta data */
    DWORD_TO_BYTES( f_pcontextSST->rgbSlotData + __CB_DECL( DRM_SST_SLOT_METADATA_SIZE ), 
                    f_pcontextSST->cbSlotData );        
    
    if( DRM_SST_SLOT_HEADER_SIZE + f_cbData <= SIZEOF( f_pcontextSST->rgbSlotData ) )
    {
        /* Copy to in memory buffer to minimize calls to SlotWrite */
        DRM_BYT_CopyBytes( f_pcontextSST->rgbSlotData, DRM_SST_SLOT_DATA_OFFSET, f_pbData, 0, f_cbData );
        ChkDR( DRM_HDS_SlotWrite( &f_pcontextSST->oSlotContext, 
                                   f_pcontextSST->cbSlotData, 
                                   f_pcontextSST->rgbSlotData, 
                                  &cbWritten ) );
        if ( cbWritten != f_pcontextSST->cbSlotData )
        {
            ChkDR( DRM_E_FILEWRITEERROR );
        }
    }
    else
    {

        /*
        **  Write HMAC to secstore
        */
        ChkDR( DRM_HDS_SlotWrite( &f_pcontextSST->oSlotContext, 
                                   DRM_SST_SLOT_HEADER_SIZE, 
                                   f_pcontextSST->rgbSlotData, 
                                  &cbWritten ) );
        if ( cbWritten != DRM_SST_SLOT_HEADER_SIZE )
        {
            ChkDR( DRM_E_FILEWRITEERROR );
        }
        
        /*
        **  Write data to slot
        */
        ChkDR( DRM_HDS_SlotWrite( &f_pcontextSST->oSlotContext, 
                                   f_cbData, 
                                   f_pbData, 
                                  &cbWritten ) );        
        if( cbWritten != f_cbData )
        {
            ChkDR( DRM_E_FILEWRITEERROR );
        }
    }

#if !_DATASTORE_WRITE_THRU
    ChkDR( DRM_HDS_CommitNamespace(&f_pcontextSST->oNsContext) );
#endif

ErrorExit:
    return dr;
}

/******************************************************************************
** 
** Function :   DRM_SST_CloseLockedSlot
** 
** Synopsis :   Closes the slot, releasing lock in the process
** 
** Arguments :  f_pcontextSST - secure store context
** 
** Returns :    DRM_SUCCESS 
** 
** Notes :      
** 
******************************************************************************/
DRM_RESULT DRM_API DRM_SST_CloseLockedSlot( IN  DRM_SECSTORE_CONTEXT *f_pcontextSST )
{
    DRM_RESULT  dr  = DRM_SUCCESS;

    ChkArg( f_pcontextSST != NULL );
    
    /*
    **  Close the slot - release lock if the caller holds the lock for the 
    **  secure store
    */
    if (f_pcontextSST->fLocked)
    {
        DRM_HDS_CloseSlot( &f_pcontextSST->oSlotContext );
        DRM_HDS_CloseNamespace( &f_pcontextSST->oNsContext );
        f_pcontextSST->fLocked = FALSE;
    }      
        
ErrorExit:
    return dr;
}

/******************************************************************************
** 
** Function :   DRM_SST_GetData
** 
** Synopsis :   
** 
** Arguments :  
** 
** Returns :    
** 
** Notes :      
** 
******************************************************************************/
DRM_RESULT DRM_API DRM_SST_GetData(
    IN       DRM_SECSTORE_CONTEXT   *f_pcontextSST,
    IN const DRM_ID                 *f_pKey1,
    IN const DRM_ID                 *f_pKey2,
    IN const DRM_BYTE                f_rgbPassword[__CB_DECL(SHA_DIGEST_LEN)],
    IN       eDRM_SECURE_STORE_TYPE  f_eType,
    IN       DRM_HDS_CONTEXT        *f_pcontextHDS,
       OUT   DRM_BYTE               *f_pbData,
       OUT   DRM_DWORD              *f_pcbData )
{
    DRM_RESULT  dr      = DRM_SUCCESS;
    DRM_DWORD   cbData  = 0;
    
    /*
    **  All other parameters will be checked by internal functions
    **  This is just a wrapper over them
    */
    ChkArg( f_pcbData != NULL );
    
    ChkDR( DRM_SST_OpenAndLockSlot( f_pcontextHDS, 
                                    f_eType, 
                                    f_pKey1, 
                                    f_pKey2, 
                                    f_rgbPassword,
                                    0, 
                                    f_pcontextSST,
                                    &cbData ) );
    if ( *f_pcbData < cbData)
    {
        *f_pcbData = cbData;
        dr = DRM_E_BUFFERTOOSMALL;
        goto ErrorExit;        
    }
    
    /*
    **  Read data now
    */
    ChkDR( DRM_SST_GetLockedData( f_pcontextSST, 
                                  f_pbData, 
                                  f_pcbData ) );
    
ErrorExit:
    
    /*
    **  Release lock
    */ 
    DRM_SST_CloseLockedSlot( f_pcontextSST );    
    return dr;
}

/******************************************************************************
** 
** Function :   DRM_SST_SetData
** 
** Synopsis :   
** 
** Arguments :  
** 
** Returns :    
** 
** Notes :      
** 
******************************************************************************/
DRM_RESULT DRM_API DRM_SST_SetData(
    IN       DRM_SECSTORE_CONTEXT   *f_pcontextSST,
    IN const DRM_ID                 *f_pKey1,
    IN const DRM_ID                 *f_pKey2,
    IN const DRM_BYTE                f_rgbPassword[__CB_DECL(SHA_DIGEST_LEN)],
    IN       eDRM_SECURE_STORE_TYPE  f_eType,
    IN       DRM_HDS_CONTEXT        *f_pcontextHDS,
       OUT   DRM_BYTE               *f_pbData,
    IN       DRM_DWORD               f_cbData )
{
    DRM_RESULT  dr      = DRM_SUCCESS;
    
    /*
    **  Parameters will be checked by internal functions
    **  This is just a wrapper over them
    */
    ChkDR( DRM_SST_OpenAndLockSlot( f_pcontextHDS, 
                                    f_eType, 
                                    f_pKey1, 
                                    f_pKey2, 
                                    f_rgbPassword,
                                    DRM_SECURE_STORE_CREATE_IF_NOT_EXISTS, 
                                    f_pcontextSST,
                                    NULL ) );    
    /*
    **  Set data now
    */
    ChkDR( DRM_SST_SetLockedData( f_pcontextSST,
                                  f_cbData, 
                                  f_pbData ) );
    
ErrorExit:

    /*
    **  Release lock
    */ 
    DRM_SST_CloseLockedSlot( f_pcontextSST );
    return dr;

}

static DRM_RESULT _UpdateAttributeTokenInSlot( 
    DRM_SECSTORE_CONTEXT *pcontextSST, 
    DRM_CONST_STRING     *pdstrAttribute,
    TOKEN                *pToken,
    DRM_BOOL              fNewAttribute )
{
    DRM_RESULT dr        = DRM_SUCCESS;    
    DRM_DWORD  dwOffset  = 0;
    DRM_WORD   cbString  = 0;
    DRM_DWORD   cbDataLen       = 0;
    DRM_DWORD   dwDataOffset    = 0;
    TOKEN       token;

    if ( pcontextSST->dwSlotVersion == 0)
    {
        cbDataLen       = pcontextSST->cbSlotData - DRM_SST_SLOT_V0_HEADER_SIZE;
        dwDataOffset    = DRM_SST_SLOT_V0_DATA_OFFSET;        
    }
    else
    {
        cbDataLen       = pcontextSST->cbSlotData - DRM_SST_SLOT_HEADER_SIZE;
        dwDataOffset    = DRM_SST_SLOT_DATA_OFFSET; 
    }
    while( dwOffset < cbDataLen )
    {
        DRM_WORD cbString = 0;

        DRM_BYT_CopyBytes( &cbString, 0, pcontextSST->rgbSlotData, dwDataOffset + dwOffset, SIZEOF( cbString ) );
        FIX_ENDIAN_WORD( cbString );        
        
        dwOffset += SIZEOF( cbString );

        if( cbString == CB_DSTR( pdstrAttribute )
         && DRM_MAX_ATTRIBUTE_STRING_LENGTH >= pdstrAttribute->cchString )
        {
            DRM_WCHAR rgchString[DRM_MAX_ATTRIBUTE_STRING_LENGTH];

            DRM_BYT_CopyBytes( rgchString, 0, pcontextSST->rgbSlotData, dwDataOffset + dwOffset, cbString );
            dwOffset += cbString;

            /* It is the right size, see if it is the right string */
            if( 0 == MEMCMP(rgchString, PB_DSTR( pdstrAttribute ), CB_DSTR( pdstrAttribute ) ) )
            {
                DRM_BYT_CopyBytes( &token, 0, pcontextSST->rgbSlotData, dwDataOffset + dwOffset, SIZEOF( TOKEN ) );
                _PrepareTokenForRead( &token );                

                /* Apply the diff */
                if( fNewAttribute )
                {
                    /* Yikes!  This is a new attribute for our context but someother context has already
                        written it.  Tread here very carfully */
                    /* The valid scenarios for this is relative experiation, or saveddatetime being updated,
                        or perhaps metering or playlist burns.  In the time cases we will do nothing.  In the count
                        cases we can safely add the diff */                    
                    switch( pToken->TokenType )
                    {
                    case TOKEN_LONG:
                        /* Ensure that it really is a positive add though, because that is the only valid scenario. */
                        if( pToken->val.lValue < 0 )
                        {
                            dr = CPRMEXP_UPDATE_FAILURE;
                            goto ErrorExit;
                        }
                        token.val.lValue += pToken->val.lValue;
                        break;
                    case TOKEN_DATETIME:
                        /* Do nothing */
                        break;
                    }
                }
                else
                {
                    /* Update the result token with the delta data */
                    switch( pToken->TokenType )
                    {
                    case TOKEN_LONG:
                        /* NewDiff = NewValue - ( OldDiff + CachedValue ) */                        
                        token.val.lValue += pToken->val.lValue;
                        if( token.val.lValue < 0 )
                        {
                            /* Really slim case that if someone used their last playcount in one session, but didn't get flushed,
                                and it was later set to 0 in another context, we could roll over to a huge count.  We 
                                don't want our smart diff delay write to have a hole where users turn 1 playcount into
                                0x8FFFFFFF playcounts! */
                            dr = CPRMEXP_UPDATE_FAILURE;
                            goto ErrorExit;
                        }
                        break;
                    case TOKEN_DATETIME:
                        /* The delta value has been set, just return that. */
                        token.val.u64DateTime = pToken->val.u64DateTime;
                    }
                }
                _PrepareTokenForWrite( &token );
                DRM_BYT_CopyBytes( pcontextSST->rgbSlotData, dwDataOffset + dwOffset, &token, 0, SIZEOF( TOKEN ) );
                dwOffset += SIZEOF( TOKEN );
                dr = DRM_SUCCESS;
                goto ErrorExit;
            }
            dwOffset += SIZEOF( TOKEN );
        }
        else
        {            
            dwOffset += ( cbString + SIZEOF( TOKEN ) );
        }
    }

    /* If we got here then the attribute was found in the slot.  This is good for new attributes, bad for non-new attributes */
    if( !fNewAttribute )
    {
        ChkDR( CPRMEXP_UPDATE_FAILURE );
    }
    
    dwOffset                 = pcontextSST->cbSlotData;
    pcontextSST->cbSlotData += SIZEOF( DRM_WORD ) 
                             + CB_DSTR( pdstrAttribute ) 
                             + SIZEOF( TOKEN );

    if( pcontextSST->cbSlotData > DRM_SEC_STORE_MAX_SLOT_SIZE )
    {
        ChkDR( DRM_E_SECURESTORE_FULL );
    }
    
    cbString = (DRM_WORD) CB_DSTR( pdstrAttribute );    
    FIX_ENDIAN_WORD( cbString );
    MEMCPY( &token, pToken, SIZEOF( TOKEN ) );
    _PrepareTokenForWrite( &token );

    DRM_BYT_CopyBytes( pcontextSST->rgbSlotData, dwOffset, &cbString,                 0, SIZEOF( cbString ) );
    dwOffset += SIZEOF( cbString );
    DRM_BYT_CopyBytes( pcontextSST->rgbSlotData, dwOffset, PB_DSTR( pdstrAttribute ), 0, CB_DSTR( pdstrAttribute ) );
    dwOffset += CB_DSTR( pdstrAttribute );
    DRM_BYT_CopyBytes( pcontextSST->rgbSlotData, dwOffset, &token,                    0, SIZEOF( TOKEN ) );    

ErrorExit:
    return dr;
}

static DRM_RESULT _ApplyDiffsToStore( 
    IN     DRM_SECSTORE_CONTEXT *pcontextSST, 
    IN OUT DRM_SECSTORE_CONTEXT *pcontextSSTFromDisk )
{
    DRM_RESULT dr     = DRM_SUCCESS;
    DRM_WORD   iCount = 0;    

    for( iCount = 0; iCount < pcontextSST->wNumOriginalAttributes; iCount++ )
    {
        if( ( pcontextSST->rgAttributes[iCount].TokenDelta.TokenType == TOKEN_LONG &&
              pcontextSST->rgAttributes[iCount].TokenDelta.val.lValue    != 0 ) ||
            ( pcontextSST->rgAttributes[iCount].TokenDelta.TokenType == TOKEN_DATETIME &&
              !DRM_UI64Eql( pcontextSST->rgAttributes[iCount].TokenDelta.val.u64DateTime, DRM_UI64(0) ) ) )
        {
            ChkDR( _UpdateAttributeTokenInSlot( pcontextSSTFromDisk,
                                                &(pcontextSST->rgAttributes[iCount].dstrAttribute),
                                                &(pcontextSST->rgAttributes[iCount].TokenDelta),
                                                FALSE ) );
        }
    }

    for( iCount = pcontextSST->wNumOriginalAttributes; iCount < pcontextSST->wNumAttributes; iCount++ )
    {
        if( ( pcontextSST->rgAttributes[iCount].TokenDelta.TokenType == TOKEN_LONG ) ||
            ( pcontextSST->rgAttributes[iCount].TokenDelta.TokenType == TOKEN_DATETIME ) )
        {
            ChkDR( _UpdateAttributeTokenInSlot( pcontextSSTFromDisk, 
                                                &(pcontextSST->rgAttributes[iCount].dstrAttribute),
                                                &(pcontextSST->rgAttributes[iCount].TokenDelta),
                                                TRUE ) );        
        }
    }

ErrorExit:
    return dr;
}

                    
DRM_RESULT DRM_API DRM_SST_CloseKey( 
    IN       DRM_SECSTORE_CONTEXT *pcontextSST, 
    IN       DRM_HDS_CONTEXT      *pcontextHDS )
{
    DRM_RESULT   dr                 = DRM_SUCCESS;
    DRM_DWORD    cbWritten          = 0;    
    DRM_DWORD    dwSlotMetaData     = 0;
    DRM_BOOL     fCloseStore        = FALSE;
    DRM_SECSTORE_CONTEXT contextSST = { 0 };
    HMAC_CONTEXT oHmacContext;

    ChkArg (pcontextSST          != NULL
         && pcontextHDS          != NULL
         && pcontextSST->fInited != FALSE);
    
    MEMCPY( &contextSST.rgbKey1, &pcontextSST->rgbKey1, SIZEOF( contextSST.rgbKey1 ) );
    MEMCPY( &contextSST.rgbKey2, &pcontextSST->rgbKey2, SIZEOF( contextSST.rgbKey2 ) );
    contextSST.pNamespaceId = pcontextSST->pNamespaceId;
        
    ChkDR( _OpenAndVerifySlot( &contextSST, pcontextHDS, pcontextSST->rgbPassword, pcontextSST->eType, FALSE, FALSE ) );    
    fCloseStore = TRUE;

    /* The slot hash of the now opened slot is valid.  Go about the process of applying diff's to the 
       attributes in the slot */    
    ChkDR( _ApplyDiffsToStore( pcontextSST, &contextSST ) );

    /* Update the size in the slot meta data */
    if ( contextSST.dwSlotVersion == 0 )
    {
        /*
        **  We must update the format of this slot before writing it to disc
        */ 

        /*  Set the metadata first  */
        BYTES_TO_DWORD( dwSlotMetaData, contextSST.rgbSlotData );
        DRM_SST_SET_SLOT_METADATA( dwSlotMetaData );
        DWORD_TO_BYTES( contextSST.rgbSlotData, dwSlotMetaData );

        /*  Move the rest of the data down by a DWORD       */
        /*  We have enough space in the bufer to do this    */
        DRM_BYT_MoveBytes( contextSST.rgbSlotData, 
                           DRM_SST_SLOT_DATA_OFFSET, 
                           contextSST.rgbSlotData, 
                           DRM_SST_SLOT_V0_DATA_OFFSET, 
                           contextSST.cbSlotData - DRM_SST_SLOT_V0_HEADER_SIZE );

        /*  Increment the slotsize by a DWORD   */
        contextSST.cbSlotData += DRM_SST_SLOT_SIZEDATA_SIZE;
    }

    /*  Update the size data in the buffer  */        
    DWORD_TO_BYTES( contextSST.rgbSlotData + __CB_DECL( DRM_SST_SLOT_METADATA_SIZE ), 
                    contextSST.cbSlotData );     

    ChkDR( DRM_HMAC_Init    ( &oHmacContext, pcontextSST->rgbPassword,                                        SHA_DIGEST_LEN ) );
    ChkDR( DRM_HMAC_Update  ( &oHmacContext, contextSST.rgbSlotData + __CB_DECL( DRM_SST_SLOT_DATA_OFFSET ), contextSST.cbSlotData - DRM_SST_SLOT_HEADER_SIZE) );
    ChkDR( DRM_HMAC_Finalize( &oHmacContext, contextSST.rgbSlotData + __CB_DECL( DRM_SST_SLOT_HASH_OFFSET ), SHA_DIGEST_LEN ) );

    /* Write the new hash back to disk */    
    if( contextSST.cbSlot < contextSST.cbSlotData )
    {
        ChkDR( DRM_HDS_SlotResize( &contextSST.oSlotContext, contextSST.cbSlotData ) );
    }
    ChkDR( DRM_HDS_SlotSeek  ( &contextSST.oSlotContext, 0,                     eDRM_HDS_SEEKSET,        NULL ) );
    ChkDR( DRM_HDS_SlotWrite ( &contextSST.oSlotContext, contextSST.cbSlotData, contextSST.rgbSlotData, &cbWritten ) );

#if !_DATASTORE_WRITE_THRU
    ChkDR( DRM_HDS_CommitNamespace(&contextSST.oNsContext) );
#endif
    
ErrorExit:    
    if( fCloseStore )
    {
        DRM_HDS_CloseSlot     ( &contextSST.oSlotContext );
        DRM_HDS_CloseNamespace( &contextSST.oNsContext   );
    }    
    return dr;
}


DRM_RESULT DRM_API DRM_SST_GetTokenValue(
    IN       DRM_SECSTORE_CONTEXT *pcontextSST,
    IN const DRM_CONST_STRING     *pdstrAttribute,
       OUT   TOKEN                *pToken )
{
    DRM_RESULT                  dr       = DRM_S_FALSE; /* Return DRM_S_FALSE to indicate that it couldn't be found */
    DRM_DWORD                   dwCount  = 0;
    /*Some compilers remove memcpy while optimizing which can cause datatype misalignment exceptions*/
    DRM_BYTE *pbOptimizeBugByte = NULL; 

    ChkArg (pcontextSST          != NULL
         && pcontextSST->fInited != FALSE
         && pToken               != NULL);

    ChkDRMString( pdstrAttribute );

    for( dwCount = 0; dwCount < pcontextSST->wNumAttributes; dwCount++ )
    {
        if( pdstrAttribute->cchString == pcontextSST->rgAttributes[dwCount].dstrAttribute.cchString 
         && 0 == MEMCMP( PB_DSTR( pdstrAttribute ), 
                         PB_DSTR( &pcontextSST->rgAttributes[dwCount].dstrAttribute ), 
                         CB_DSTR( pdstrAttribute ) ) )
        {
            /* We matched the string value. Return the token value.*/
            pbOptimizeBugByte = (DRM_BYTE*)pcontextSST->rgAttributes[dwCount].pTokenValue; 
            MEMCPY( pToken, pbOptimizeBugByte, SIZEOF( TOKEN ) );
            _PrepareTokenForRead( pToken );

            /* Update the result token with the delta data */
            switch( pToken->TokenType )
            {
            case TOKEN_LONG:
                /* NewDiff = NewValue - ( OldDiff + CachedValue ) */
                pToken->val.lValue += pcontextSST->rgAttributes[dwCount].TokenDelta.val.lValue;
                break;
            case TOKEN_DATETIME:
                if( !DRM_UI64Eql( pcontextSST->rgAttributes[dwCount].TokenDelta.val.u64DateTime, DRM_UI64( 0 ) ) )
                {
                    /* The delta value has been set, just return that. */
                    pbOptimizeBugByte = (DRM_BYTE*)&(pcontextSST->rgAttributes[dwCount].TokenDelta); 
                    MEMCPY( pToken, pbOptimizeBugByte, SIZEOF( TOKEN ) );
                }
            }
            
            dr = DRM_SUCCESS; /* DRM_SUCCESS indicates that the value was found */
            break;
        }
    }

ErrorExit:
    return dr;
}

DRM_RESULT DRM_API DRM_SST_SetTokenValue(
    IN       DRM_SECSTORE_CONTEXT *pcontextSST,
    IN const DRM_CONST_STRING     *pdstrAttribute,
    IN const TOKEN                *pToken )
{
    DRM_RESULT dr = DRM_S_FALSE; /* Return DRM_S_FALSE to indicate that it couldn't be found */
    DRM_DWORD  dwCount  = 0;
    DRM_WORD   cbString = 0;
    TOKEN token;
    /*Some compilers remove memcpy while optimizing which can cause datatype misalignment exceptions*/
    DRM_BYTE *pbOptimizeBugByte = NULL; 

    ChkArg (pcontextSST          != NULL
        &&  pToken               != NULL
        &&  pcontextSST->fInited != FALSE
        &&  (pToken->TokenType   == TOKEN_LONG 
          || pToken->TokenType   == TOKEN_DATETIME));

    ChkDRMString( pdstrAttribute );

    /* Search to see if the token already exists */
    for( dwCount = 0; dwCount < pcontextSST->wNumAttributes; dwCount++ )
    {
        if( pdstrAttribute->cchString == pcontextSST->rgAttributes[dwCount].dstrAttribute.cchString 
         && 0 == MEMCMP( PB_DSTR( pdstrAttribute ), 
                         PB_DSTR( &pcontextSST->rgAttributes[dwCount].dstrAttribute ), 
                         CB_DSTR( pdstrAttribute ) ) )
        {
            /* We matched the string value. Update the delta.*/
            pbOptimizeBugByte = (DRM_BYTE*)pcontextSST->rgAttributes[dwCount].pTokenValue; 
            MEMCPY( &token, pbOptimizeBugByte, SIZEOF( TOKEN ) );
            _PrepareTokenForRead( &token );
            if( pToken->TokenType != token.TokenType )
            {
                /* You can't change the token type!! */
                ChkDR( DRM_E_WRONG_TOKEN_TYPE );
            }

            pcontextSST->rgAttributes[dwCount].TokenDelta.TokenType = token.TokenType;
            switch( token.TokenType )
            {
                case TOKEN_LONG:
                    /* NewDiff = NewValue - ( OldDiff + CachedValue ) */
                    pcontextSST->rgAttributes[dwCount].TokenDelta.val.lValue = pToken->val.lValue - (pcontextSST->rgAttributes[dwCount].TokenDelta.val.lValue + token.val.lValue);
                    break;
                case TOKEN_DATETIME:
                    pbOptimizeBugByte = (DRM_BYTE*)&(pcontextSST->rgAttributes[dwCount].TokenDelta); 
                    MEMCPY( pbOptimizeBugByte, pToken, SIZEOF( TOKEN ) );
                    break;
            }

            dr = DRM_SUCCESS;
            goto ErrorExit;
        }
    }
        
    /* Attribute doesn't exist.  Try to create a new one.  Be weary or running out of slot space, as well as 
       cached attribute structure space */
    if( pcontextSST->wNumAttributes == DRM_MAX_ATTRIBUTES_PER_SST_KEY )
    {
        ChkDR( DRM_E_SECURESTORE_FULL );
    }
    if( ( pcontextSST->cbSlotData + SIZEOF( DRM_WORD ) + CB_DSTR( pdstrAttribute ) + SIZEOF ( TOKEN ) ) > DRM_SEC_STORE_MAX_SLOT_SIZE )
    {
        /* Not enough room in the slot */
        ChkDR( DRM_E_SECURESTORE_FULL );
    }

    /* Fill in the data in the slot buffer and the cache attribute array */
    ZEROMEM(       &pcontextSST->rgAttributes[pcontextSST->wNumAttributes], 
            SIZEOF( pcontextSST->rgAttributes[pcontextSST->wNumAttributes] ) );

    cbString = (DRM_WORD) CB_DSTR( pdstrAttribute );
    FIX_ENDIAN_WORD( cbString );
    MEMCPY( &(pcontextSST->rgbSlotData[__CB_DECL(pcontextSST->cbSlotData)]), &cbString, SIZEOF( cbString ) );
    pcontextSST->cbSlotData += SIZEOF( cbString );
    FIX_ENDIAN_WORD( cbString );
    
    pcontextSST->rgAttributes[pcontextSST->wNumAttributes].dstrAttribute.cchString  = pdstrAttribute->cchString;
    pcontextSST->rgAttributes[pcontextSST->wNumAttributes].dstrAttribute.pwszString = (DRM_WCHAR*) &(pcontextSST->rgbSlotData[__CB_DECL(pcontextSST->cbSlotData)]);
    MEMCPY( &(pcontextSST->rgbSlotData[__CB_DECL(pcontextSST->cbSlotData)]), pdstrAttribute->pwszString, cbString );
    pcontextSST->cbSlotData += cbString;

    MEMCPY (&(pcontextSST->rgAttributes[dwCount].TokenDelta), pToken, SIZEOF( TOKEN ) );
    ZEROMEM(&token, SIZEOF( TOKEN ) );
    token.TokenType = pToken->TokenType;
    
    pcontextSST->rgAttributes[pcontextSST->wNumAttributes].pTokenValue = (TOKEN*) &(pcontextSST->rgbSlotData[__CB_DECL(pcontextSST->cbSlotData)]);
    _PrepareTokenForWrite( &token );
    MEMCPY( &(pcontextSST->rgbSlotData[__CB_DECL(pcontextSST->cbSlotData)]), &token, SIZEOF( TOKEN ) );
    pcontextSST->cbSlotData += SIZEOF( TOKEN );
    pcontextSST->wNumAttributes++;

    dr = DRM_SUCCESS;

ErrorExit:
    return dr;
}

/*****************************************************************************
** Function: DRM_SST_DeleteKey
**
** Synopsis: Delete an entry from the secure store
**
** Arguments:
**           [f_pcontextSST] -- secure store context, need not be initialized
**           [f_typeSST]     -- one of the legal enums corresponding to the 
**                              layer to open
**           [f_pid1]
**           [f_pid2]        -- major and minor "keys" of the data store to 
**                              delete
**           [f_pcontextHDS] -- initialized HDS context
**                              
** Notes:    Indiscriminate deletion of keys could open up the system for 
**           replay attacks 
*****************************************************************************/

DRM_RESULT DRM_API DRM_SST_DeleteKey(
    IN       DRM_SECSTORE_CONTEXT   *f_pcontextSST,
    IN       eDRM_SECURE_STORE_TYPE  f_typeSST,
    IN const DRM_ID                 *f_pid1,
    IN const DRM_ID                 *f_pid2,
    IN       DRM_HDS_CONTEXT        *f_pcontextHDS)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_HDS_UNIQUEKEY id2 = {0};

    ChkArg(f_pcontextSST != NULL
        && f_pcontextHDS != NULL
        && f_pid1        != NULL);

    /* Check for supported flags */

    ChkArg(f_typeSST == SECURE_STORE_LICENSE_DATA             
        || f_typeSST == SECURE_STORE_GLOBAL_DATA              
        || f_typeSST == SECURE_STORE_REVOCATION_DATA          
        || f_typeSST == SECURE_STORE_METERING_DATA            
        || f_typeSST == SECURE_STORE_DEVICE_REGISTRATION_DATA 
        || f_typeSST == SECURE_STORE_PLAYLIST_BURNING_DATA);

    switch (f_typeSST)
    {
    case SECURE_STORE_METERING_DATA:
        f_pcontextSST->pNamespaceId = &METER_STORE_NAMESPACE;
        break;
        
    case SECURE_STORE_PLAYLIST_BURNING_DATA:
        f_pcontextSST->pNamespaceId = &PLAYLIST_BURN_STORE_NAMESPACE;
        break;
        
    case SECURE_STORE_DEVICE_REGISTRATION_DATA:
        f_pcontextSST->pNamespaceId = &DEVICE_REGISTRATION_STORE_NAMESPACE;
        break;
        
    case SECURE_STORE_GLOBAL_DATA: /* Fall through */
    case SECURE_STORE_REVOCATION_DATA:
    case SECURE_STORE_LICENSE_DATA:
        f_pcontextSST->pNamespaceId = &SEC_STORE_NAMESPACE;
        break;
    }

    if( f_pid2 != NULL )
    {
        MEMCPY( &id2, f_pid2, SIZEOF( id2 ) );
    }
    ChkDR(DRM_HDS_OpenNamespace( f_pcontextHDS, 
                                 f_pcontextSST->pNamespaceId, 
                                 eDRM_HDS_OPEN_EXISTING | eDRM_HDS_LOCKWAIT,
                                 0,
                                 &f_pcontextSST->oNsContext));
                               
    ChkDR(DRM_HDS_DeleteSlot(&f_pcontextSST->oNsContext,
                             (DRM_HDS_HASHKEY   *) f_pid1, 
                             &id2,
                             NULL,
                             TRUE));

ErrorExit:
    return dr;
}


DRM_RESULT DRM_API DRM_SST_GetAllData( 
    IN     DRM_SECSTORE_CONTEXT *pcontextSST,
    OUT    DRM_BYTE             *pbData,
    IN OUT DRM_DWORD            *pcbData )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_DWORD cbRequired = 0;
    DRM_DWORD   cbHeader    = 0;

    ChkArg (pcontextSST          != NULL
        &&  pcbData              != NULL
        &&  pcontextSST->fInited != FALSE);

    cbHeader = ( pcontextSST->dwSlotVersion == 0 )
             ? DRM_SST_SLOT_V0_HEADER_SIZE
             : DRM_SST_SLOT_HEADER_SIZE;
    
    cbRequired = pcontextSST->cbSlotData - cbHeader;

    if (  pbData == NULL
      || *pcbData < cbRequired)
    {        
        *pcbData = cbRequired;
        dr = DRM_E_BUFFERTOOSMALL;
        goto ErrorExit;
    }

    MEMCPY (pbData, pcontextSST->rgbSlotData + __CB_DECL( cbHeader ), cbRequired);

    *pcbData = cbRequired;
    dr = DRM_SUCCESS;

ErrorExit:
    return dr;
}


DRM_RESULT DRM_API DRM_SST_OpenEnumerator(
    IN        eDRM_SECURE_STORE_TYPE    eType,
    IN  const DRM_ID                   *pKey1,
    OUT       DRM_SECSTOREENUM_CONTEXT *pcontextSSTEnum,
    IN        DRM_HDS_CONTEXT          *pcontextHDS,
    IN        DRM_BOOL                  fExclusiveLock)
{
    DRM_RESULT dr=DRM_SUCCESS;      
    const DRM_HDS_NAMESPACE *pNamespaceId = NULL;
    DRM_DWORD  dwLockMode = eDRM_HDS_LOCKWAIT;

    ChkArg( pcontextSSTEnum && pcontextHDS );
    if ( fExclusiveLock )
    {
        dwLockMode |= eDRM_HDS_LOCKEXCLUSIVE;
    }
    else
    {
        dwLockMode |= eDRM_HDS_LOCKSHARED;
    }

    /* Check for supported flags */
    if (!( eType == SECURE_STORE_LICENSE_DATA             ||
           eType == SECURE_STORE_GLOBAL_DATA              ||
           eType == SECURE_STORE_REVOCATION_DATA          ||
           eType == SECURE_STORE_METERING_DATA            ||
           eType == SECURE_STORE_DEVICE_REGISTRATION_DATA ||
           eType == SECURE_STORE_PLAYLIST_BURNING_DATA ) )
    {
        dr = DRM_E_INVALIDARG;
        goto ErrorExit;
    }

    ZEROMEM(pcontextSSTEnum, SIZEOF(pcontextSSTEnum));

    switch( eType )
    {
    case SECURE_STORE_METERING_DATA:
        pNamespaceId = &METER_STORE_NAMESPACE;
        break;
    case SECURE_STORE_PLAYLIST_BURNING_DATA:
        pNamespaceId = &PLAYLIST_BURN_STORE_NAMESPACE;
        break;
    case SECURE_STORE_DEVICE_REGISTRATION_DATA:
        pNamespaceId = &DEVICE_REGISTRATION_STORE_NAMESPACE;
        break;
    case SECURE_STORE_GLOBAL_DATA: /* Fall through */
    case SECURE_STORE_REVOCATION_DATA:
    case SECURE_STORE_LICENSE_DATA:
        pNamespaceId = &SEC_STORE_NAMESPACE;
        break;
    }

    dr = DRM_HDS_OpenNamespace( pcontextHDS, 
                                pNamespaceId, 
                                eDRM_HDS_OPEN_EXISTING | eDRM_HDS_LOCKWAIT,
                                0,
                                &pcontextSSTEnum->oNsContext );
    if ( dr == DRM_E_HDSNAMESPACENOTFOUND )
    {  
        dr = DRM_SUCCESS;
        pcontextSSTEnum->fInited = TRUE;
        pcontextSSTEnum->eMode   = eSSTEnumNone;
        goto ErrorExit;
    }
    ChkDR( dr );

    dr = DRM_HDS_InitSlotEnum( &pcontextSSTEnum->oNsContext, 
                               (DRM_HDS_HASHKEY*)pKey1, 
                               dwLockMode, 
                               &pcontextSSTEnum->oHdsEnumContext);
    if( dr == DRM_E_HDSSLOTNOTFOUND )
    {
        /* No Slots found. */
        pcontextSSTEnum->eMode = eSSTEnumNone;
        dr = DRM_SUCCESS;
    }
    else if( DRM_FAILED( dr ) )
    {
        goto ErrorExit;
    }
    else if ( pKey1 )
    {
        pcontextSSTEnum->eMode = eSSTEnumFiltered;
    }
    else
    {
        pcontextSSTEnum->eMode = eSSTEnumNatural;        
    }

    pcontextSSTEnum->fCurrIsValid = FALSE;
    pcontextSSTEnum->fInited      = TRUE;
    pcontextSSTEnum->eType        = eType;

ErrorExit:    
    return dr;
}


/*
**
*/
DRM_RESULT DRM_API DRM_SST_EnumNext( 
    IN  DRM_SECSTOREENUM_CONTEXT *pcontextSSTEnum,
    OUT DRM_ID                   *pKey2,
    OUT DRM_DWORD                *pcbData )
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD  cbSlot = 0;
    DRM_DWORD  cbHeader         = 0;
    DRM_DWORD  cbRead           = 0;   
    DRM_DWORD  dwSlotMetaData   = 0;

    ChkArg (pcontextSSTEnum           != NULL
         && pcontextSSTEnum->fInited  != NULL
         && pcbData         != NULL
         && pKey2           != NULL);

    if ( pcontextSSTEnum->eMode == eSSTEnumNone )
    {
        dr = DRM_E_NOMORE;
        goto ErrorExit;            
    }

    *pcbData = 0;

    dr = DRM_HDS_SlotEnumNext(&pcontextSSTEnum->oHdsEnumContext, 
                              &pcontextSSTEnum->oSlotContext, 
                              &pcontextSSTEnum->oCurrKID, 
                              &pcontextSSTEnum->oCurrLID, 
                              &cbSlot );
    if ( dr == DRM_E_NOMORE )
    {
        goto ErrorExit;
    }
    else if ( dr == DRM_E_HDSBLOCKMISMATCH 
           || dr == DRM_E_HDSSLOTNOTFOUND )
    {
        ChkDR(DRM_E_SECURESTORE_CORRUPT);
    }
    ChkDR(dr);

    MEMCPY(pKey2->rgb, pcontextSSTEnum->oCurrLID.rgb, DRM_ID_SIZE);    

    pcontextSSTEnum->fCurrIsValid = TRUE;
    
    /*
    **  Figure out the slot version
    */
    ChkDR( DRM_HDS_SlotRead( &pcontextSSTEnum->oSlotContext, 
                             DRM_SST_SLOT_METADATA_SIZE, 
                             (DRM_BYTE *)&dwSlotMetaData, 
                             &cbRead ) );
    if( cbRead != DRM_SST_SLOT_METADATA_SIZE )
    {
        dr = DRM_E_FILEREADERROR;
        goto ErrorExit;
    }
    FIX_ENDIAN_DWORD( dwSlotMetaData );
    
    *pcbData = ( ( dwSlotMetaData & DRM_SST_SLOT_VERSION_MASK ) == 0 )
             ? cbSlot - DRM_SST_SLOT_V0_HEADER_SIZE
             : cbSlot - DRM_SST_SLOT_HEADER_SIZE;
    ChkDR(DRM_HDS_CloseSlot(&pcontextSSTEnum->oSlotContext));
    
ErrorExit:
    return dr;
}

/******************************************************************************
** Function :   DRM_SST_EnumDeleteCurrent
** Synopsis :   Delete the current enumerated slot
******************************************************************************/
DRM_RESULT DRM_API DRM_SST_EnumDeleteCurrent( 
    IN       DRM_SECSTOREENUM_CONTEXT *pcontextSSTEnum,
    IN OUT   DRM_SECSTORE_CONTEXT     *pcontextSST)
{
    DRM_RESULT dr               = DRM_SUCCESS;
    DRM_DWORD  cbSlot           = 0;
    DRM_DWORD  cbHeader         = 0;
    DRM_DWORD  cbRead           = 0;   
    DRM_DWORD  dwSlotMetaData   = 0;
    DRM_DWORD  cbData           = 0;
    
    ChkArg (pcontextSSTEnum          != NULL
         && pcontextSSTEnum->fInited != NULL);

    ChkBOOL( pcontextSSTEnum->eMode != eSSTEnumNone, DRM_E_NOMORE);

    dr = DRM_HDS_SlotEnumReloadCurrent(&pcontextSSTEnum->oHdsEnumContext, 
                                       &pcontextSST->oSlotContext, 
                                       &pcontextSSTEnum->oCurrKID, 
                                       &pcontextSSTEnum->oCurrLID, 
                                       &cbSlot);
    ChkBOOL(dr != DRM_E_NOMORE, DRM_E_NOMORE);
    ChkBOOL((dr != DRM_E_HDSBLOCKMISMATCH && dr != DRM_E_HDSSLOTNOTFOUND), DRM_E_SECURESTORE_CORRUPT);
    ChkDR(dr);

    dr = DRM_HDS_SlotEnumDeleteCurrent(&pcontextSSTEnum->oHdsEnumContext, 
                                       &pcontextSST->oSlotContext);
    DRM_HDS_CloseSlot( &pcontextSST->oSlotContext );
    pcontextSSTEnum->fCurrIsValid = FALSE;
    pcontextSST->fInited = FALSE;
    
ErrorExit:
    return dr;
}


/******************************************************************************
** Function :   DRM_SST_EnumLoadCurrent
** Synopsis :   Load the current slot in memory
******************************************************************************/
DRM_RESULT DRM_API DRM_SST_EnumLoadCurrent( 
    IN       DRM_SECSTOREENUM_CONTEXT *pcontextSSTEnum,
    IN OUT   DRM_SECSTORE_CONTEXT     *pcontextSST,
    IN const DRM_BYTE                  rgbPassword[__CB_DECL(SHA_DIGEST_LEN)],
       OUT   DRM_ID                   *pKey2,
    IN OUT DRM_DWORD                  *pcbData)
{
    DRM_RESULT dr               = DRM_SUCCESS;
    DRM_DWORD  cbSlot           = 0;
    DRM_DWORD  cbHeader         = 0;
    DRM_DWORD  cbRead           = 0;   
    DRM_DWORD  dwSlotMetaData   = 0;
    DRM_DWORD  cbData           = 0;
    
    ChkArg (pcontextSSTEnum          != NULL
         && pcontextSSTEnum->fInited != NULL
         && pKey2                    != NULL
         && pcbData                  != NULL);

    ChkBOOL( pcontextSSTEnum->eMode != eSSTEnumNone, DRM_E_NOMORE);

    dr = DRM_HDS_SlotEnumReloadCurrent(&pcontextSSTEnum->oHdsEnumContext, 
                                       &pcontextSST->oSlotContext, 
                                       &pcontextSSTEnum->oCurrKID, 
                                       &pcontextSSTEnum->oCurrLID, 
                                       &cbSlot);
    ChkBOOL(dr != DRM_E_NOMORE, DRM_E_NOMORE);
    ChkBOOL((dr != DRM_E_HDSBLOCKMISMATCH && dr != DRM_E_HDSSLOTNOTFOUND), DRM_E_SECURESTORE_CORRUPT);
    ChkDR(dr);

    MEMCPY(pKey2->rgb, pcontextSSTEnum->oCurrLID.rgb, DRM_ID_SIZE);
    pcontextSSTEnum->fCurrIsValid = TRUE;
    
    /*
    **  Figure out the slot version
    */
    ChkDR( DRM_HDS_SlotRead( &pcontextSST->oSlotContext, 
                             DRM_SST_SLOT_METADATA_SIZE, 
                             (DRM_BYTE *)&dwSlotMetaData, 
                             &cbRead ) );
    if ( cbRead != DRM_SST_SLOT_METADATA_SIZE )
    {
        dr = DRM_E_FILEREADERROR;
        goto ErrorExit;
    }
    FIX_ENDIAN_DWORD( dwSlotMetaData );

    cbData = ( ( dwSlotMetaData & DRM_SST_SLOT_VERSION_MASK ) == 0 )
             ? cbSlot - DRM_SST_SLOT_V0_HEADER_SIZE
             : cbSlot - DRM_SST_SLOT_HEADER_SIZE;

    /* read the slot data */
    pcontextSST->cbSlot = cbData;

    DRM_HDS_SlotSeek(&pcontextSST->oSlotContext, 0, eDRM_HDS_SEEKSET, NULL);
    dr = _LoadSlot(pcontextSST, FALSE, pcontextSSTEnum->eType, rgbPassword);
    if ( DRM_SUCCEEDED(dr) )
    {
        *pcbData = cbData;
    }
    DRM_HDS_CloseSlot( &pcontextSST->oSlotContext );
    pcontextSST->fInited = TRUE;
    
ErrorExit:
    return dr;
}
/*****************************************************************************
** Function: DRM_SST_CreateGlobalStorePassword
**
** Synopsis: use the keyeed to create the hash used as the secure store password 
**           for the global store
**
** Arguments:
**           [f_rgbSeed]     -- buffer to hold keyseed 
**           [f_rgbPassword] -- buffer to hold Password
**           [f_pcontextBBX] -- initialized BBX context
*****************************************************************************/

DRM_RESULT DRM_API DRM_SST_CreateGlobalStorePassword(
    OUT DRM_BYTE  f_rgbPasswordSST [__CB_DECL(SHA_DIGEST_LEN)],
    IN  DRM_BYTE *f_pbContextBBX)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_BYTE   rgbSeed [__CB_DECL(SHA_DIGEST_LEN)];

    ChkDR(OEM_GetSecureStoreGlobalPasswordSeed(rgbSeed));

    ChkDR(DRM_BBX_HashValue(rgbSeed, 
                            SHA_DIGEST_LEN-1,
                            f_rgbPasswordSST,
                            (DRM_BB_CONTEXT *) f_pbContextBBX));

ErrorExit:
    return dr;
}

/*****************************************************************************
** Function: DRM_SST_CreateLicenseStatePassword
**
** Synopsis: use the LID to create the hash used as the secure store password 
**           for this license
**
** Arguments:
**           [f_rgbLID]         -- the LID 
**           [f_rgbPasswordSST] -- buffer to receive the password
**           [f_pcontextBBX]    -- initialized BBX context
*****************************************************************************/

DRM_RESULT DRM_API DRM_SST_CreateLicenseStatePassword(
    IN  DRM_LID  *f_plid,
    OUT DRM_BYTE  f_rgbPasswordLST [__CB_DECL(SHA_DIGEST_LEN)],
    IN  DRM_BYTE *f_pbContextBBX)
{
    DRM_RESULT dr = DRM_SUCCESS;

    ChkDR(DRM_BBX_HashValue((DRM_BYTE*) f_plid, 
                            SIZEOF (DRM_LID), 
                            f_rgbPasswordLST, 
                            (DRM_BB_CONTEXT *) f_pbContextBBX)); 

ErrorExit:
    return dr;
}



