/**@@@+++@@@@******************************************************************
**
** Microsoft Windows Media
** Copyright (C) Microsoft Corporation. All rights reserved.
**
***@@@---@@@@******************************************************************
*/
#include <drmcommon.h>
#include <drmutilities.h>
#include <drmcrt.h>
#include <drmcontextsizes.h>
#include <drmpkcrypto.h>
#include <drmcipher.h>
#include <drmsha1.h>
#include <drmmanager.h>
#include <oemimpl.h>
#include "CRYS.h"
#include "DX_VOS_Mem.h"
#include "parser_mlli_oper.h"


#ifdef DX_WMDRM_USE_CRYS
extern DRM_RESULT DRM_API DRM_PK_Sign_sep(
										IN       DRM_VOID  *f_pContext,
										IN const PRIVKEY   *f_privkey,
										IN const DRM_BYTE  *f_pbData,
										OUT      DRM_BYTE   f_rgbSignature[__CB_DECL(PK_ENC_SIGNATURE_LEN)] );
#else
extern DRM_RESULT DRM_API DRM_PK_Sign_sep(
										IN       DRM_VOID  *f_pContext,
										IN const PRIVKEY   *f_privkey,
										IN const DRM_BYTE  *f_pbData,
										IN       DRM_DWORD  f_cbData,
										IN       DRM_BOOL   isHash,
										OUT      DRM_BYTE   f_rgbSignature[__CB_DECL(PK_ENC_SIGNATURE_LEN)] );
#endif


#define HASH_RESIDUAL_LEN 6





static DRM_RESULT _GetDevicePrivkey( 
    IN OUT DRM_BB_CONTEXT *f_pcontextBBX,
       OUT PRIVKEY        *f_pDevPrivkey)
{
    DRM_RESULT dr = DRM_SUCCESS;
    PRIVKEY    GCPrivkey;

    ChkArg( f_pcontextBBX != NULL 
         && f_pDevPrivkey != NULL);

    ChkDR(OEM_GetGroupCertPrivateKey(&GCPrivkey));

    ChkDR( DRM_PK_SymmetricCrypt( (DRM_BYTE*)&GCPrivkey,
                                   SIZEOF( GCPrivkey ),
                                   SIZEOF(PRIVKEY),
                                   f_pcontextBBX->cachedCertValues.m_blobDevicePrivkey,
                                   SIZEOF(PRIVKEY),
                                   (DRM_BYTE *)f_pDevPrivkey ) );

ErrorExit:
    ZEROMEM((DRM_BYTE*)&GCPrivkey, SIZEOF(PRIVKEY));  /* trash the key in memory */
    return dr;    
}

#ifdef DX_SEP_RC4_HW_ENGINE
/*  
    Decrypt a license that was encryted for privacy reasons (not 
    security reasons) while traveling on the net during license
    acquisition.
	DX version - The encrypted key is being passed in one parameter sepratly from the encrypted license.
				 pbEncryptedLicenseKey - encrypted key buffer (length PK_ENC_PLAINTEXT_LEN)

*/
DRM_BOOL DRM_API DRM_BBX_DecryptLicense_SEP(
    IN     DRM_BYTE       pbEncryptedLicenseKey[__CB_DECL(PK_ENC_CIPHERTEXT_LEN)],
    IN     DRM_BYTE       *pbEncryptedLicenseTable,    /* MLLI first in table base address */
    IN     DRM_DWORD      cbEncryptedLicenseTableSize, /* MLLI first in table size */
    IN     DRM_BYTE       *pbDecryptedLicenseTable,    /*MLLI first out table base address */ 
    IN     DRM_DWORD      cbDecryptedLicenseTableSize, /* MLLI first out table size */
    IN     DRM_DWORD      cbLicenseSize,				/*MLLI first table data size */
    IN     DRM_BB_CONTEXT *pcontextBBX )
{
    DRM_RESULT dr = DRM_SUCCESS;    
    DRM_BYTE   rgbKey[__CB_DECL(PK_ENC_PLAINTEXT_LEN)];
    PRIVKEY    privkey;
    RC4_KEYSTRUCT *prc4KS = NULL;

    ChkArg( pcontextBBX        != NULL
         && pcontextBBX->fInited );

    /* use device privkey to decrypt the license blob */
    ChkDR(_GetDevicePrivkey(pcontextBBX, &privkey));
    ChkDR(DRM_PK_Decrypt(pcontextBBX->CryptoContext.rgbCryptoContext, 
                        &privkey, 
                         pbEncryptedLicenseKey, 
                         rgbKey));

    if( GET_BYTE(rgbKey, 1) != PKSYMM_ALG_TYPE_RC4 )
    {
        ChkDR( DRM_E_UNSUPPORTEDALGORITHM );
    }



    prc4KS = (RC4_KEYSTRUCT*)(pcontextBBX->CryptoContext.rgbCryptoContext);
	
    ChkDR( CRYS_RC4_Init(&prc4KS->rc4CTX.rc4Hw, rgbKey + __CB_DECL(2), GET_BYTE(rgbKey, 0)) );
    ChkDR( DX_SEP_PARSER_RC4_StreamOper(&prc4KS->rc4CTX.rc4Hw,
										pbEncryptedLicenseTable,
										cbEncryptedLicenseTableSize,
										pbDecryptedLicenseTable,
										cbDecryptedLicenseTableSize,
										cbLicenseSize) );

ErrorExit:
    ZEROMEM(&privkey, SIZEOF(PRIVKEY));
    return (DRM_SUCCEEDED(dr) ? TRUE : FALSE);
}

#endif

/*  Used for computing the secure store password. */
DRM_RESULT DRM_API DRM_BBX_HashValue_SEP(
    IN        DRM_BYTE *pbTableAdd,   /*MLLI first table base address*/
    IN        DRM_DWORD cbTableSize,  /*MLLI first table size */
    IN        DRM_DWORD dataInSize,   /*MLLI first table data size*/
	IN        DRM_BYTE *cbHeadData,   /*Tail data(used for hash end of data treatment */
	IN        DRM_DWORD cbHeadDataSize,   /*size of the tail data*/
	IN        DRM_BYTE *cbTailData,   /*Tail data(used for hash end of data treatment */
	IN        DRM_DWORD cbTailDataSize,   /*size of the tail data*/
    OUT       DRM_BYTE        rgbHashValue [__CB_DECL(SHA_DIGEST_LEN)],
    IN        DRM_BB_CONTEXT *pcontextBBX )
{
    DRM_RESULT dr = DRM_SUCCESS;
    PRIVKEY    privkey;
    SHA_CONTEXT *pcontextSHA;
	CRYS_HASH_Result_t       HashResultBuff;


    ChkArg( pbTableAdd      != NULL
         && cbTableSize       > 0 
         && ((dataInSize       > 0 )|| (cbHeadDataSize > 0) || (cbTailDataSize > 0))
         && rgbHashValue != NULL
         && pcontextBBX  != NULL
         && pcontextBBX->fInited );

    /* use device privkey */
    ChkDR(_GetDevicePrivkey(pcontextBBX, &privkey));

    pcontextSHA = (SHA_CONTEXT*)&(pcontextBBX->CryptoContext);
    ChkDR( CRYS_HASH_Init(&pcontextSHA->HashCTX,CRYS_HASH_SHA1_mode) );
    ChkDR( CRYS_HASH_Update(&pcontextSHA->HashCTX,privkey.x, SIZEOF(PRIVKEY)) );    
    ChkDR( CRYS_HASH_Finish( &pcontextSHA->HashCTX, HashResultBuff) );

	/*  Now hash the given data (pValue). */
    ChkDR( CRYS_HASH_Init( &pcontextSHA->HashCTX,CRYS_HASH_SHA1_mode) );
    ChkDR( CRYS_HASH_Update(&pcontextSHA->HashCTX, (DxUint8_t*)HashResultBuff, HASH_RESIDUAL_LEN) );

	ChkDR( DX_SEP_PARSER_HASH_UpdateOper(&pcontextSHA->HashCTX,
		                                 cbHeadData,cbHeadDataSize,cbTailData,cbTailDataSize,
										 pbTableAdd,
										 cbTableSize,
										 dataInSize) );

	ChkDR( CRYS_HASH_Finish(&pcontextSHA->HashCTX, HashResultBuff) );

	DX_VOS_FastMemCpy(rgbHashValue,HashResultBuff,SHA_DIGEST_LEN);
ErrorExit:
    ZEROMEM(&privkey, SIZEOF(PRIVKEY));
    return dr;
}



/******************************************************************************
** Function :   DRM_BBX_SignData
** Synopsis :   Sign data with the machine private key 
** Arguments :  f_pcontextBBX       - Blackbox context
**              f_pbData            - Data to be signed
**              f_cbData            - Size of data to be signed
**              f_rgbSignature      - Signature
******************************************************************************/
DRM_RESULT DRM_API DRM_BBX_SignData_SEP(
    IN       DRM_BB_CONTEXT *f_pcontextBBX,
    IN       DRM_BYTE *pbTableAdd,   /*MLLI first table base address*/
    IN       DRM_DWORD cbTableSize,  /*MLLI first table size */
    IN       DRM_DWORD dataInSize,   /*MLLI first table data size*/
	IN       DRM_BYTE *pbTailDataAdd,   /*Tail data used for the end of the hash*/
	IN       DRM_DWORD cbTailDataSize,  /*size of the tail data */
    IN       DRM_DWORD       f_ePrivKeyType,
       OUT   DRM_BYTE        f_rgbSignature[__CB_DECL(PK_ENC_SIGNATURE_LEN)])
{
    PRIVKEY     privkey;
    DRM_RESULT  dr = DRM_SUCCESS;
    SHA_CONTEXT hashCon;
    CRYS_HASH_Result_t       HmacResultBuff;

    ChkArg( f_pcontextBBX  != NULL 
         && f_ePrivKeyType == eBBX_PRIVKEY_MACHINE
         && f_pcontextBBX->fInited );
    
    ChkDR( _GetDevicePrivkey(f_pcontextBBX, &privkey) );

    ChkDR( CRYS_HASH_Init(&hashCon.HashCTX, CRYS_HASH_SHA1_mode) );

	ChkDR( DX_SEP_PARSER_HASH_UpdateOper(&hashCon.HashCTX,
		                                 0,0,pbTailDataAdd,cbTailDataSize,
										 pbTableAdd,
										 cbTableSize,
										 dataInSize));

	ChkDR( CRYS_HASH_Finish( &hashCon.HashCTX,
                             HmacResultBuff));

#ifdef DX_WMDRM_USE_CRYS
	ChkDR( DRM_PK_Sign_sep(f_pcontextBBX->CryptoContext.rgbCryptoContext, 
						    &privkey, 
                            (DRM_BYTE*)HmacResultBuff,
                            f_rgbSignature) ); 
#else
	ChkDR( DRM_PK_Sign_sep( f_pcontextBBX->CryptoContext.rgbCryptoContext, 
						    &privkey, 
                            (const DRM_BYTE*)HmacResultBuff,
                            SHA_DIGEST_LEN,
						    TRUE,
                            f_rgbSignature) ); 
#endif
ErrorExit:
    ZEROMEM(&privkey, SIZEOF(PRIVKEY));
    return dr;
}


/*********************************************************************
**
**  Function:  DRM_BBX_SymmetricSign
**
**  Synopsis:  Creates a symmetric signature (that only the current blackbox can verify) over the data provided 
**             by the caller
**
**  Arguments:  
**     [f_pcontextBBX] -- Pointer to an initialized blackbox context
**     [f_pbData]      -- Pointer to the data to sign
**     [f_cbData]      -- Length of f_pbData in DRM_BYTEs
**     [f_rgbSymSig]   -- Buffer to place the resultant 'symmetric signature'
**
*********************************************************************/

DRM_RESULT DRM_API DRM_BBX_SymmetricSign_SEP(
    IN       DRM_BB_CONTEXT *f_pcontextBBX,
    IN		 DRM_BYTE       *f_pbDataTable, /*MLLI first table base address */
    IN       DRM_DWORD       f_cbDataTable, /*MLLI first table size */
    IN       DRM_DWORD       f_cbData, /*MLLI first table data size */
	IN		 DRM_BYTE       *f_pbTailData, /*Tail data used for the end of the hash */
	IN       DRM_DWORD       f_cbTailData, /*size of the tail data */
    OUT      DRM_BYTE        f_rgbSymSig[__CB_DECL( SHA_DIGEST_LEN )] )
{
    PRIVKEY     privkey;
    HMAC_CONTEXT hmac;
    DRM_RESULT  dr = DRM_SUCCESS;
    CRYS_HASH_Result_t       HmacResultBuff;


    ChkArg( f_pcontextBBX  != NULL 
         && f_pbDataTable  != NULL
         && f_cbDataTable  != 0
         && ((f_cbData       != 0) || (f_cbTailData != 0))
         && f_pcontextBBX->fInited );
    
    ChkDR( _GetDevicePrivkey(f_pcontextBBX, &privkey) );

    ChkDR( CRYS_HMAC_Init(&hmac.HmacCTX, CRYS_HASH_SHA1_mode, privkey.x, PK_ENC_PRIVATE_KEY_LEN ) );

	ChkDR( DX_SEP_PARSER_HMAC_UpdateOper(&hmac.HmacCTX,
		                                 0,0,f_pbTailData,f_cbTailData,
										f_pbDataTable,
										f_cbDataTable,
										f_cbData));

	ChkDR( CRYS_HMAC_Finish( &hmac.HmacCTX,
                             HmacResultBuff));

	DX_VOS_FastMemCpy(f_rgbSymSig,HmacResultBuff,SHA_DIGEST_LEN);
ErrorExit:
    ZEROMEM(&privkey, SIZEOF(PRIVKEY));
    return dr;
}


/*********************************************************************
**
**  Function:  DRM_BBX_SymmetricVerify
**
**  Synopsis:  Verifies a symmetric signtaure that was created by a call to DRM_BBX_SymmetricSign
**
**  Arguments:  
**     [f_pcontextBBX] -- Pointer to an initialized blackbox context
**     [f_pbData]      -- Pointer to the data to sign
**     [f_cbData]      -- Length of f_pbData in DRM_BYTEs
**     [f_pslk]        -- Optional.  If this parameter is provided the caller is asking to verify
**                        the signature with a BBX protected SLK key.
**     [f_rgbSymSig]   -- Buffer containing the signtature to compare against.
**
*********************************************************************/

DRM_RESULT DRM_API DRM_BBX_SymmetricVerify_SEP(
    IN       DRM_BB_CONTEXT *f_pcontextBBX,
    IN		 DRM_BYTE       *f_pbDataTable, /*MLLI first table base address */
    IN       DRM_DWORD       f_cbDataTable, /*MLLI first table size */
    IN       DRM_DWORD       f_cbData,      /*MLLI first table data size */
	IN		 DRM_BYTE       *f_pbTailData, /*Tail data used for the end of the hash */
	IN       DRM_DWORD       f_cbTailData, /*size of the tail data */
    IN const DRM_SLK        *f_pslk,
    IN const DRM_BYTE        f_rgbSymSig[__CB_DECL( SHA_DIGEST_LEN )] )
{
    PRIVKEY     privkey;
    DRM_RESULT  dr = DRM_SUCCESS;
    HMAC_CONTEXT hmac;
    CRYS_HASH_Result_t       HmacResultBuff;
   
    ChkArg( f_pcontextBBX  != NULL 
         && f_pbDataTable  != NULL
         && f_cbDataTable  != 0
         && ((f_cbData     != 0) || (f_cbTailData != 0))
         && f_pcontextBBX->fInited );
    
    ChkDR( _GetDevicePrivkey(f_pcontextBBX, &privkey) );

    if( f_pslk == NULL )
    {
		ChkDR( CRYS_HMAC_Init(&hmac.HmacCTX, CRYS_HASH_SHA1_mode, privkey.x, PK_ENC_PRIVATE_KEY_LEN ) );

		ChkDR( DX_SEP_PARSER_HMAC_UpdateOper(&hmac.HmacCTX,
			                                 0,0,f_pbTailData,f_cbTailData,
											f_pbDataTable,
											f_cbDataTable,
											f_cbData));

		ChkDR( CRYS_HMAC_Finish( &hmac.HmacCTX,
								 HmacResultBuff));

		if( MEMCMP( f_rgbSymSig, HmacResultBuff, SHA_DIGEST_LEN ) != 0 )
		{
			dr = DRM_E_INVALID_SIGNATURE;
		}

		
    }
    else
    {
        DRM_SLK slkClear;

        ChkDR( DRM_PK_SymmetricCrypt( (DRM_BYTE*)&privkey,
                                      SIZEOF( privkey ),                                  
                                      SIZEOF( DRM_SLK ),   /* Only reencrypt the fist decrypt 8 bytes */
									  (DRM_BYTE*) f_pslk,
								      SIZEOF(slkClear),
									  (DRM_BYTE*)&slkClear ) );

		ChkDR( CRYS_HMAC_Init(&hmac.HmacCTX, CRYS_HASH_SHA1_mode, slkClear.rgb, SIZEOF( slkClear ) ) );

		ChkDR( DX_SEP_PARSER_HMAC_UpdateOper(&hmac.HmacCTX,
			                                 0,0,f_pbTailData,f_cbTailData,
											f_pbDataTable,
											f_cbDataTable,
											f_cbData));
		ChkDR( CRYS_HMAC_Finish( &hmac.HmacCTX ,
								 HmacResultBuff));

		if( MEMCMP( f_rgbSymSig, HmacResultBuff, SHA_DIGEST_LEN ) != 0 )
		{
			dr = DRM_E_INVALID_SIGNATURE;
		}

	}

ErrorExit:
    ZEROMEM(&privkey, SIZEOF(PRIVKEY));
    return dr;
}


