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


#include <drmcommon.h>
#include <drmsha1.h>
#include <drmbytemanip.h>
#ifndef DX_WMDRM_USE_CRYS

/*
	OEMNOTE: There are optimizations that could be done.
	1.  The loop ranges in SHA are static so the loop can be unrolled.  This will make larger code but will be faster.
	2.  If loop is unrolled the ft and K functions can be put inline as to not incur a function call overhead
*/
	
/* These macros are for readability and should make sense when looking at the standard */
#define A 0
#define B 1
#define C 2
#define D 3
#define E 4

/* Define S (circular shift operator) */
#define S ROTATE_LEFT

/******************************************************************************/
static DRM_VOID DRM_API _PackDRMDWORD(
    IN const DRM_DWORD *dwFrom,
    IN       DRM_DWORD  dwCount, 
    OUT      DRM_BYTE  *bOut)
{
	DRM_UINT i = 0;
    DRM_UINT j = 0;
	for(; i<dwCount; i++)
	{
		PUT_BYTE(bOut, j, (DRM_BYTE)((dwFrom[i] >> 24) & 0xff)); j++;
        PUT_BYTE(bOut, j, (DRM_BYTE)((dwFrom[i] >> 16) & 0xff)); j++;
        PUT_BYTE(bOut, j, (DRM_BYTE)((dwFrom[i] >> 8 ) & 0xff)); j++;
        PUT_BYTE(bOut, j, (DRM_BYTE)((dwFrom[i]      ) & 0xff)); j++;
	}
}

/******************************************************************************/

static DRM_VOID DRM_API _GetDRMDWORD(
    OUT      DRM_DWORD *dwTo,
    IN       DRM_DWORD  dwCount,
    IN const DRM_BYTE  *bIn )
{
	DRM_UINT i = 0;
    DRM_UINT j = 0;
	for( ; i < dwCount; i++, j+=4)
	{
        dwTo[i] = ( ( (DRM_DWORD) GET_BYTE(bIn,j)   ) << 24 ) | 
                  ( ( (DRM_DWORD) GET_BYTE(bIn,j+1) ) << 16 ) |
                  ( ( (DRM_DWORD) GET_BYTE(bIn,j+2) ) << 8 )  |
                  (   (DRM_DWORD) GET_BYTE(bIn,j+3) );
	}
}


/******************************************************************************/

static DRM_INT _ft(DRM_INT b, DRM_INT c, DRM_INT d, DRM_INT t)
{
	DRMASSERT(t<80);
	if(t >= 60)
		return (b^c^d);
	if(t>=40)
		return ((b&c)|(b&d)|(c&d));
	if(t>=20)
		return (b^c^d);
	if(t>=0)
		return ((b&c) | ((~b)&d));

	return 0;	/* If valid input we should never hit this */

}

/******************************************************************************/

static DRM_DWORD _K(DRM_SHORT t)
{

	DRMASSERT(t<80);
	if(t >= 60)
		return 0xCA62C1D6;
	if(t>=40)
		return 0x8F1BBCDC;
	if(t>=20)
		return 0x6ED9EBA1;
	if(t>=0)
		return 0x5A827999;

	return 0;	/* If valid input we should never hit this */
}

/******************************************************************************/

/* SHA operates on 512 bit blocks which is 16-dword sized blocks.  (16*32 = 512) */

/**********************************************************************
** Function:		_sha1
**
** Synopsis:	Meat of sha, this is the actual mathematical processing.
**
** Arguments:	[ABCDE] -- current state numbers of sha
**				[bInput] -- 64 byte block to put through the sha process
**
** Returns:		None
**
** Notes:		This version of SHA1 is written to reduce processor instructions
**				but the tradeoff is the 80 DWORD stack buffer.
**
***********************************************************************/
static DRM_VOID _sha1(
    IN       DRM_DWORD ABCDE[5],
    IN const DRM_BYTE  bInput[__CB_DECL(SHA_BLOCK_SIZE)] )
{
    DRM_DWORD Buf2[5];
    DRM_DWORD W[80];
    DRM_SHORT cCount;
    DRM_DWORD TEMP;


    _GetDRMDWORD(W,16,bInput);
    MEMCPY((DRM_BYTE*)Buf2, (DRM_BYTE*)ABCDE, SIZEOF(DRM_DWORD) * 5);
    for(cCount = 16; cCount < 80; cCount++)
    {
        TEMP = W[cCount-3] ^ W[cCount-8] ^ W[cCount-14] ^ W[cCount-16];
        W[cCount] = S(TEMP,1);
    }
    for(cCount = 0; cCount < 80; cCount++ )
    {
        TEMP = S(ABCDE[A],5);
        TEMP += _ft(ABCDE[B],ABCDE[C],ABCDE[D], cCount);
        TEMP += ABCDE[E];
        TEMP += W[cCount];
        TEMP += _K(cCount);
        ABCDE[E] = ABCDE[D];
        ABCDE[D] = ABCDE[C];
        ABCDE[C] = S(ABCDE[B],30);
        ABCDE[B] = ABCDE[A];
        ABCDE[A] = TEMP;
    }
    for (cCount = 0; cCount < 5; cCount++)
    {
        Buf2[cCount] += ABCDE[cCount];
    }
    MEMCPY((DRM_BYTE*)ABCDE, (DRM_BYTE*)Buf2, SIZEOF(DRM_DWORD) * 5);
}

/******************************************************************************/
DRM_VOID DRM_API DRM_SHA_Init(
    IN OUT SHA_CONTEXT* pShaContext )
{
    /*Zero out the buffer*/
	ZEROMEM((DRM_BYTE*)pShaContext, SIZEOF(SHA_CONTEXT));	
	
    /* Set the initial magic numbers */
    pShaContext->ABCDE[A] = 0x67452301;
	pShaContext->ABCDE[B] = 0xEFCDAB89;
	pShaContext->ABCDE[C] = 0x98BADCFE;
	pShaContext->ABCDE[D] = 0x10325476;
	pShaContext->ABCDE[E] = 0xC3D2E1F0;
}

/******************************************************************************/
DRM_VOID DRM_API DRM_SHA_Update(
    IN const DRM_BYTE    *pbData,
    IN       DRM_DWORD    cbData,
    IN OUT   SHA_CONTEXT *pShaContext )
{
    DRM_SHA_UpdateOffset( pbData, 0, cbData, pShaContext );
    
    return;
}

DRM_VOID DRM_API DRM_SHA_UpdateOffset(
    IN const DRM_BYTE    *pbData,
    IN       DRM_DWORD    ibData,
    IN       DRM_DWORD    cbData,
    IN OUT   SHA_CONTEXT *pShaContext )
{
    /* If we aren't a multiple of 64 bytes we should save the last bytes to odr buffer
    ** and sha what is left
    */
    DRM_DWORD dwTempLen;


    /* How many bytes do we have remaining? */
    dwTempLen = pShaContext->dwLowByteCount & (SHA_BLOCK_SIZE - 1);
    pShaContext->dwLowByteCount += cbData;

    if(pShaContext->dwLowByteCount < cbData)
    {
        /* We overflowed and wrapped around.  This means
        ** we need to increment the high order byte counter 
        */
        pShaContext->dwHighByteCount++;
    }

    if (   dwTempLen > 0 
        && cbData   >= (SHA_BLOCK_SIZE - dwTempLen) )
    {
        /* We have enough to complete the last block.  Fill it and sha it */
        DRM_BYT_CopyBytes(pShaContext->bTempBuffer, dwTempLen, pbData, ibData, SHA_BLOCK_SIZE-dwTempLen);
        _sha1(pShaContext->ABCDE,pShaContext->bTempBuffer);        
        ibData += SHA_BLOCK_SIZE - dwTempLen;
        cbData -= SHA_BLOCK_SIZE - dwTempLen;

        dwTempLen = 0;
    }

    /* Sha each portion of the buffer that is big enough */
    while(cbData>=SHA_BLOCK_SIZE)
    {
#if SIXTEEN_BIT_ADDRESSING
        DRM_BYT_CopyBytes(pShaContext->bTempBuffer, 0, pbData, ibData, SHA_BLOCK_SIZE);
        _sha1( pShaContext->ABCDE,pShaContext->bTempBuffer);
#else
        _sha1( pShaContext->ABCDE,pbData + __CB_DECL(ibData));
#endif
        ibData += SHA_BLOCK_SIZE;
        cbData -= SHA_BLOCK_SIZE;
    }

    if(cbData)
    {
        DRM_BYT_CopyBytes(pShaContext->bTempBuffer, dwTempLen, pbData, ibData, cbData);
    }
}

/******************************************************************************/

DRM_VOID DRM_API DRM_SHA_Finalize(
    IN  SHA_CONTEXT *pShaContext,
    OUT DRM_BYTE     rgbDigest[__CB_DECL(SHA_DIGEST_LEN)] )
{
	DRM_DWORD dwTempLen;
	DRM_DWORD dwTotalBitLen[2];
    DRM_BYTE  bPaddingBuffer[__CB_DECL(SHA_BLOCK_SIZE+12)] = {0};	/* Maximum block size we may need. */
		
	/* How many bytes do we need to make a SHA block? */
	dwTempLen = SHA_BLOCK_SIZE - (pShaContext->dwLowByteCount & 63);
	
	/* This is there enough room for the padding?.*/
	if(dwTempLen<=8)
    {
		dwTempLen += SHA_BLOCK_SIZE;
    }
	
	/*dwLowByteCount is the number of bytes so we have to multiply that by 8 to get the number of bits */
	dwTotalBitLen[1] = pShaContext->dwLowByteCount << 3;
	dwTotalBitLen[0] = (pShaContext->dwHighByteCount << 3) | 
						(pShaContext->dwLowByteCount >> 29); /* We could have overflowed so put the extra here */

    PUT_BYTE(bPaddingBuffer, 0, 0x80);	/* We add a 10000000 */ 
	
#if SIXTEEN_BIT_ADDRESSING
    {
        DRM_BYTE rgbTemp[__CB_DECL(2*SIZEOF(DRM_DWORD))];
        /* 
        ** We do the packing to a temporary buffer because the offset into
        ** the padding buffer could be odd, and _PackDRMDWORD doesn't handle
        ** that case
        */
        _PackDRMDWORD(dwTotalBitLen,2,rgbTemp);
        DRM_BYT_CopyBytes( bPaddingBuffer, dwTempLen-8, rgbTemp, 0, 2*SIZEOF(DRM_DWORD) );
    }

#else

    /* Last 2 DWORDS are for the total bit length */
    _PackDRMDWORD(dwTotalBitLen,2,&(bPaddingBuffer[(dwTempLen-8)]));

#endif

	DRM_SHA_Update(bPaddingBuffer, dwTempLen, pShaContext);
	
	/* Extract the digest and save it. */
    _PackDRMDWORD(pShaContext->ABCDE,5,rgbDigest);		
	return ;
}

#else
/******************************************************************************/
DRM_VOID DRM_API DRM_SHA_Init(
    IN OUT SHA_CONTEXT* pShaContext )
{
	CRYS_HASH_Init(&pShaContext->HashCTX ,CRYS_HASH_SHA1_mode);
	return;
}

/******************************************************************************/
DRM_VOID DRM_API DRM_SHA_Update(
    IN const DRM_BYTE    *pbData,
    IN       DRM_DWORD    cbData,
    IN OUT   SHA_CONTEXT *pShaContext )
{
#ifdef DX_ARM_INTEGRATOR_DEBUG
	DRM_DWORD loop;
	DRM_DWORD dataLen;
	loop = cbData/0x4000;
	if(cbData%0x4000)
	{
		loop++;
	}
	while(loop)
	{
		if(cbData > 0x4000)
		{
			dataLen = 0x4000;	
		}
		else
		{
			dataLen = cbData;	
		}
    	CRYS_HASH_Update( &pShaContext->HashCTX,(DxUint8_t*)pbData, dataLen);
		pbData = pbData + dataLen;
		cbData = cbData - dataLen;
		loop--;
	}
#else
    	CRYS_HASH_Update( &pShaContext->HashCTX,(DxUint8_t*)pbData, cbData);
#endif
    return;
}

DRM_VOID DRM_API DRM_SHA_UpdateOffset(
    IN const DRM_BYTE    *pbData,
    IN       DRM_DWORD    ibData,
    IN       DRM_DWORD    cbData,
    IN OUT   SHA_CONTEXT *pShaContext )
{
    CRYS_HASH_Update( &pShaContext->HashCTX,(DxUint8_t*)&pbData[ibData], cbData);
	return;
}

/******************************************************************************/
/*In order to avoid memory overflow the Hash Finish operation is done on local 
buffer and than only the SHA1 result is being copied to the output buffer */
DRM_VOID DRM_API DRM_SHA_Finalize(
    IN  SHA_CONTEXT *pShaContext,
    OUT DRM_BYTE     rgbDigest[__CB_DECL(SHA_DIGEST_LEN)] )
{
	CRYS_HASH_Result_t tHashRes;
	
	CRYS_HASH_Finish(&pShaContext->HashCTX,tHashRes);

	DX_VOS_FastMemCpy(rgbDigest,tHashRes,SHA_DIGEST_LEN);
	return;
}


#endif
