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

/*
chain-sum MAC scheme 4.5: multiply-&-swap rounds plus sum (reversible -- reversal code included)
*/
#include <drmcommon.h>
#include <drmcrt.h>
#include <drmcbc.h>

/* pairwise independent function and summing step */
#define MP_C_STEP_P(pbData,L1, L2, L3, L4, L5, L6, t, sum) \
{\
    DRM_DWORD   dwTemp = 0;\
    BYTES_TO_DWORD( dwTemp, pbData );\
    pbData += __CB_DECL(SIZEOF(DRM_DWORD));\
    t += dwTemp; \
    t *= L1; \
    t = WORDSWAP(t); \
    t *= L2; \
    t = WORDSWAP(t); \
    t *= L3; \
    t = WORDSWAP(t); \
    t *= L4; \
    t = WORDSWAP(t); \
    t *= L5; \
    t += L6; \
    sum += t; \
}


#define MP_C_STEP(Data,L1, L2, L3, L4, L5, L6, t, sum) \
    t += Data; \
    t *= L1; \
    t = WORDSWAP(t); \
    t *= L2; \
    t = WORDSWAP(t); \
    t *= L3; \
    t = WORDSWAP(t); \
    t *= L4; \
    t = WORDSWAP(t); \
    t *= L5; \
    t += L6; \
    sum += t;

DRM_VOID DRM_API DRM_CBC_Mac(
    IN        DRM_BYTE      *pbData,
    IN        DRM_DWORD      cBlocks,
    OUT       DRM_DWORD      rgdwKeys[2],
    IN  const DRM_CBCKey    *pCBCkey )
{    
    rgdwKeys[0] = rgdwKeys[1] = 0;
    while ( cBlocks > 0)
    {
        DRM_DWORD dw = 0;
        BYTES_TO_DWORD( dw, pbData );
        pbData += __CB_DECL(SIZEOF( DRM_DWORD ));
        MP_C_STEP(dw, pCBCkey->a1, pCBCkey->b1, pCBCkey->c1, pCBCkey->d1, pCBCkey->e1, pCBCkey->f1, rgdwKeys[1], rgdwKeys[0]);
        BYTES_TO_DWORD( dw, pbData );
        pbData += __CB_DECL(SIZEOF( DRM_DWORD ));
        MP_C_STEP(dw, pCBCkey->a2, pCBCkey->b2, pCBCkey->c2, pCBCkey->d2, pCBCkey->e2, pCBCkey->f2, rgdwKeys[1], rgdwKeys[0]);
        cBlocks -= 2;
    }
    return;
}

/*******************************************************************/
DRM_UINT DRM_API DRM_MAC_inv32( DRM_UINT n )
{
    DRM_INT x = n;
    DRM_INT i = 0;

    for (i = 0; i < 30; i++)
    {
        x *= x * n;
    }

    return (DRM_UINT)x;
}


/* step to reverse action of multiply-&-swap rounds */
#define INV_STEP_C(iL1, iL2, iL3, iL4, iL5) \
    tmp *= iL5; \
    tmp = WORDSWAP(tmp); \
    tmp *= iL4; \
    tmp = WORDSWAP(tmp); \
    tmp *= iL3; \
    tmp = WORDSWAP(tmp); \
    tmp *= iL2; \
    tmp = WORDSWAP(tmp); \
    tmp *= iL1;

#ifndef WMDRM_ON_SEP
DRM_VOID DRM_API DRM_CBC_InverseMac(
    IN OUT   DRM_BYTE   *pbData,
    IN       DRM_DWORD   cBlocks,
    IN const DRM_CBCKey *key,
    IN const DRM_CBCKey *ikey )
{
    DRM_UINT tmp = 0,tmp2 = 0;
    DRM_DWORD sum64[2];


    /* 
        Invert last two blocks (sum and 32-bit MAC).  This requires the encrypted last two
        blocks and the (dwNumBlocks-2) plaintext blocks. 
    */
    DRM_CBC_Mac(pbData, cBlocks - 2, sum64, key);
    BYTES_TO_DWORD( tmp, pbData + ((SIZEOF( DRM_UINT)*(cBlocks - 1))/CB_NATIVE_BYTE));
    sum64[0] += tmp;

    /* last word */    
    tmp -= key->f2;
    INV_STEP_C(ikey->a2, ikey->b2, ikey->c2, ikey->d2, ikey->e2);
    BYTES_TO_DWORD( tmp2, pbData + ((SIZEOF( DRM_UINT)*(cBlocks - 2))/CB_NATIVE_BYTE));
    tmp -= ( tmp2 - sum64[0] );
    DWORD_TO_BYTES( pbData + ((SIZEOF( DRM_UINT)*(cBlocks - 1)/CB_NATIVE_BYTE)), tmp );

    /* next-to-last word */
    tmp = (tmp2 - sum64[0]) - key->f1;
    INV_STEP_C(ikey->a1, ikey->b1, ikey->c1, ikey->d1, ikey->e1);
    tmp -= sum64[1];
    DWORD_TO_BYTES( pbData + ((SIZEOF( DRM_UINT)*(cBlocks - 2)/CB_NATIVE_BYTE)), tmp );
        
    return;
}
#endif //WMDRM_ON_SEP
DRM_VOID DRM_API DRM_CBC64InitState( DRM_CBCState *cbcstate ) {
  cbcstate->sum = 0; cbcstate->t = 0; cbcstate->dwBufLen = 0;
}

#ifndef WMDRM_ON_SEP
DRM_VOID DRM_API DRM_CBC64Init( DRM_CBCKey *cbckey, DRM_CBCState *cbcstate, DRM_BYTE *pKey ) {
  DRM_UINT *p = NULL;

  cbcstate->sum = 0; cbcstate->t = 0; cbcstate->dwBufLen = 0;
  p = (DRM_UINT *)pKey;
  cbckey->a1 = *p++ | 0x00000001;
  cbckey->b1 = *p++ | 0x00000001;
  cbckey->c1 = *p++ | 0x00000001;
  cbckey->d1 = *p++ | 0x00000001;
  cbckey->e1 = *p++ | 0x00000001;
  cbckey->f1 = *p++ | 0x00000001;
  cbckey->a2 = *p++ | 0x00000001;
  cbckey->b2 = *p++ | 0x00000001;
  cbckey->c2 = *p++ | 0x00000001;
  cbckey->d2 = *p++ | 0x00000001;
  cbckey->e2 = *p++ | 0x00000001;
  cbckey->f2 = *p++ | 0x00000001;
}

DRM_VOID DRM_API DRM_CBC64InvKey( DRM_CBCKey *cbckey, DRM_CBCKey *cbcInvKey ) {

  cbcInvKey->a1 = DRM_MAC_inv32( cbckey->a1 );
  cbcInvKey->a2 = DRM_MAC_inv32( cbckey->a2 );
  cbcInvKey->b1 = DRM_MAC_inv32( cbckey->b1 );
  cbcInvKey->b2 = DRM_MAC_inv32( cbckey->b2 );
  cbcInvKey->c1 = DRM_MAC_inv32( cbckey->c1 );
  cbcInvKey->c2 = DRM_MAC_inv32( cbckey->c2 );
  cbcInvKey->d1 = DRM_MAC_inv32( cbckey->d1 );
  cbcInvKey->d2 = DRM_MAC_inv32( cbckey->d2 );
  cbcInvKey->e1 = DRM_MAC_inv32( cbckey->e1 );
  cbcInvKey->e2 = DRM_MAC_inv32( cbckey->e2 );
  cbcInvKey->f1 = DRM_MAC_inv32( cbckey->f1 );
  cbcInvKey->f2 = DRM_MAC_inv32( cbckey->f2 );

}
#endif // WMDRM_ON_SEP
DRM_VOID DRM_API DRM_CBC64Update( 
    IN      DRM_CBCKey  *key, 
    IN  OUT DRM_CBCState    *cbcstate,
    IN      DRM_DWORD    cbData, 
    IN      DRM_BYTE    *pbData )
{
    DRM_DWORD    iData  = 0;
    DRM_DWORD    cbCopy = 0;
    DRM_DWORD    cbTemp  = 0;
    DRM_BYTE    *pbTemp = NULL;
    
    if ( cbcstate->dwBufLen > 0 ) 
    {
        cbCopy = min( cbData, 8 - cbcstate->dwBufLen );
        
        for ( iData=0; iData < cbCopy; iData++ )
        {
            PUT_BYTE( cbcstate->buf, 
                      cbcstate->dwBufLen + iData, 
                      GET_BYTE(pbData, iData) );
        }
        
        cbcstate->dwBufLen += cbCopy;
        if ( cbcstate->dwBufLen == 8 ) 
        {
            pbTemp = cbcstate->buf;
            MP_C_STEP_P( pbTemp, key->a1, key->b1, key->c1, key->d1, key->e1, key->f1, cbcstate->t, cbcstate->sum );
            MP_C_STEP_P( pbTemp, key->a2, key->b2, key->c2, key->d2, key->e2, key->f2, cbcstate->t, cbcstate->sum );
            cbcstate->dwBufLen = 0;
        }
    }

    cbTemp = (cbData - cbCopy) / 8;
    pbTemp = pbData + __CB_DECL(cbCopy);

    while (cbTemp > 0) 
    {
        MP_C_STEP_P( pbTemp, key->a1, key->b1, key->c1, key->d1, key->e1, key->f1, cbcstate->t, cbcstate->sum );
        MP_C_STEP_P( pbTemp, key->a2, key->b2, key->c2, key->d2, key->e2, key->f2, cbcstate->t, cbcstate->sum );
        cbTemp--;
    }

    cbTemp = cbCopy + ((cbData-cbCopy) / 8) * 8;
    if ( cbTemp < cbData ) 
    {
        for ( iData=cbTemp; iData<cbData; iData++ )
        {
            PUT_BYTE( cbcstate->buf, iData - cbTemp, GET_BYTE( pbData, iData ) );
        }
        cbcstate->dwBufLen = cbData - cbTemp;
    }
}

DRM_UINT DRM_API DRM_CBC64Finalize( DRM_CBCKey *key, DRM_CBCState *cbcstate, DRM_UINT *pKey2 ) {
    DRM_DWORD   i = 0;
    DRM_BYTE    *p = NULL;

    if ( cbcstate->dwBufLen > 0 ) 
    {
        for ( i=cbcstate->dwBufLen; i<8; i++ )
        {
            PUT_BYTE( cbcstate->buf, i, 0);
        }
        p = cbcstate->buf;
        MP_C_STEP_P( p, key->a1, key->b1, key->c1, key->d1, key->e1, key->f1, cbcstate->t, cbcstate->sum );
        MP_C_STEP_P( p, key->a2, key->b2, key->c2, key->d2, key->e2, key->f2, cbcstate->t, cbcstate->sum );
        cbcstate->dwBufLen = 0;
    }

    *pKey2 = cbcstate->t;
    return cbcstate->sum;
}



DRM_UINT DRM_API DRM_CBC64Invert( DRM_CBCKey *key, DRM_CBCKey *ikey, DRM_UINT MacA1, DRM_UINT MacA2,
        DRM_UINT MacB1, DRM_UINT MacB2, DRM_UINT *pInvKey2 )
{
    DRM_UINT tmp = 0;
    DRM_UINT yn = 0, yn1 = 0, xn = 0, xn1 = 0;

    MacA1 += MacB2;
    yn = MacB2;
    yn1 = MacB1 - MacA1;

    /* last word */
    tmp = yn - key->f2;
    INV_STEP_C(ikey->a2, ikey->b2, ikey->c2, ikey->d2, ikey->e2);
    xn = tmp - yn1;

    /* next-to-last word */
    tmp = yn1 - key->f1;
    INV_STEP_C(ikey->a1, ikey->b1, ikey->c1, ikey->d1, ikey->e1);
    xn1 = tmp - MacA2;

    *pInvKey2 = (DRM_UINT) xn1;
    return (DRM_UINT) xn;
}

