/**@@@+++@@@@******************************************************************
**
** Microsoft Windows Media
** Copyright (C) Microsoft Corporation. All rights reserved.
**
***@@@---@@@@******************************************************************
*/
#ifdef DX_WMDRM_USE_CRYS
#include <drmcommon.h>
#include <drmpkcrypto.h>
#include "pkcrypto.h"
#include "CRYS.h"

/*********************************************************************
**
**  Function:  DRM_PK_Sign
**
**  Synopsis:  Generate a digital signature with a private key
**
**  Arguments:  
**     [f_pContext]     -- Pointer to context the size of DRM_PKCRYPTO_CONTEXT_BUFFER_SIZE
**     [f_privkey]      -- Private key to create a signature with
**     [f_pbData]       -- Array of bytes to create a signature over - The data assum to be after hash
**     [f_rgbSignature] -- Buffer to hold result signature
*********************************************************************/
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)] )
{    
    DRM_RESULT dr = DRM_SUCCESS;
	
	DRM_DWORD sigLen = PK_ENC_SIGNATURE_LEN;
	DRM_UINT  keySize;
	CRYS_HASH_Result_t reverseData ;
	DRM_ECC_GLOBAL_UNION_t *EccBuff_ptr = (DRM_ECC_GLOBAL_UNION_t *)f_pContext;
	PRIVKEY privkey;
    DRM_BYTE   tmpRgbSignature[__CB_DECL(PK_ENC_SIGNATURE_LEN)];


    ChkArg( f_pContext     != NULL 
         && f_privkey      != NULL 
         && f_pbData       != NULL 
         && f_rgbSignature != NULL );

	keySize = sizeof(PRIVKEY);
	/* Set the key to big endian as the CRYS needs */
	CRYS_COMMON_ReverseMemcpy( privkey.x,(DRM_BYTE*)f_privkey->x, keySize );        

	/*build the CRYS key structure*/
	dr = CRYS_ECPKI_BuildPrivKey(CRYS_ECPKI_DomainID_WMDRM10,
								 privkey.x,
								 keySize,
								 &EccBuff_ptr->ECC_DEC_stc.UserPrivKey);
	if(dr != CRYS_OK)
	{
		dr = DRM_E_PKCRYPTO_FAILURE;
		goto ErrorExit;
	}

	/* Set the hash result in big endian as the CRYS needs */
	CRYS_COMMON_ReverseMemcpy( (DxUint8_t*)reverseData,(DxUint8_t*)f_pbData, SHA_DIGEST_LEN );        

	/* Sign the hash result */
	dr = CRYS_ECDSA_Sign(&EccBuff_ptr->ECC_SIGN_stc.SignUserContext,
						 &EccBuff_ptr->ECC_SIGN_stc.SignerPrivKey,
						 CRYS_ECPKI_AFTER_HASH_SHA1_mode,
						 (DRM_BYTE*)reverseData,
						 SHA_DIGEST_LEN,
						 tmpRgbSignature,
					     (DxUint32_t*)&sigLen); 

	if(dr != CRYS_OK)
	{
		dr = DRM_E_PKCRYPTO_FAILURE;
		goto ErrorExit;
	}
	
	/* reverse the sign result to much WMDRM format*/
	CRYS_COMMON_ReverseMemcpy( (f_rgbSignature),(DRM_BYTE*)tmpRgbSignature, PK_ENC_SIGNATURE_LEN );        
ErrorExit:
    return dr;
}
#else //DX_WMDRM_USE_CRYS
/*********************************************************************
**
**  Function:  DRM_PK_Sign
**
**  Synopsis:  Generate a digital signature with a private key
**
**  Arguments:  
**     [f_pContext]     -- Pointer to context the size of DRM_PKCRYPTO_CONTEXT_BUFFER_SIZE
**     [f_privkey]      -- Private key to create a signature with
**     [f_pbData]       -- Array of bytes to create a signature over
**     [f_cbData]       -- Length of pbBuffer in bytes
**     [f_rgbSignature] -- Buffer to hold result signature
*********************************************************************/
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)] )
{    
    DRM_RESULT dr = DRM_SUCCESS;
    DRMBIGNUM_CONTEXT_STRUCT *pContext = (DRMBIGNUM_CONTEXT_STRUCT *)f_pContext;
    DWORD rgdwPrivKey[ PK_ENC_PRIVATE_KEY_LEN / SIZEOF( DWORD )];
    DWORD ntry = 0;
    DWORDREG    lgcd   = 0;
    ecaffine_t *Gpriv2 = NULL;  /* [2*MP_LONGEST] */
    digit_t *gcd       = NULL;  /* [MP_LONGEST] */
    digit_t *priv2     = NULL;  /* [MP_LONGEST] */
    digit_t *priv2inv  = NULL;  /* [MP_LONGEST] */
    digit_t *sigc      = NULL;  /* [LNGQ] */
    digit_t *sigd      = NULL;  /* [LNGQ] */
    digit_t *privexpon = NULL;  /* [LNGQ] */
    BYTE    *buffer    = NULL;  /* [SIGN_BUF_LEN]     */
    PBIGCTX_ARG        = NULL;
    USEPKLOCK;
    
    ChkArg( f_pContext     != NULL 
         && f_privkey      != NULL 
         && f_pbData       != NULL 
         && f_cbData       != 0 
         && f_rgbSignature != NULL );

    ChkDR( PKInit( pContext ) );

    PKLOCK;

    pContext->oHeap.nStackTop = 0;
    pContext->oHeap.cbStack   = DRM_PKCRYPTO_CONTEXT_BUFFER_INTERNAL_SIZE;
    pContext->oHeap.pbStack   = pContext->rgbHeap;

    pbigctx = (struct bigctx_t *) &pContext->oHeap;

    ChkMem( gcd = (digit_t*)bignum_alloc( (SIZEOF( digit_t) * (3*LNGQ + 3*MP_LONGEST) ) 
                                         +(SIZEOF(ecaffine_t) * 2*MP_LONGEST ) 
                                         +SIGN_BUF_LEN,
                                         PBIGCTX_PASS ) );

    priv2     = gcd      + MP_LONGEST;
    priv2inv  = priv2    + MP_LONGEST;
    sigc      = priv2inv + MP_LONGEST;
    sigd      = sigc     + LNGQ;
    privexpon = sigd     + LNGQ;

    Gpriv2    = (ecaffine_t*)(privexpon + LNGQ);
    buffer    = (BYTE*)(Gpriv2      + 2 * MP_LONGEST);

    endian_reverse_dwords( (const DWORD *)f_privkey->x, rgdwPrivKey, PK_ENC_PRIVATE_KEY_LEN / SIZEOF( DWORD ) );
    dwords_to_digits( rgdwPrivKey, privexpon, LNGQDW, PBIGCTX_PASS );

    if(!isHash)
	{
        SHA_CONTEXT shadata;

        MEMSET( buffer, 0, SIGN_BUF_LEN );

        DRM_SHA_Init( &shadata );
        DRM_SHA_Update( (DRM_BYTE*) f_pbData, f_cbData, &shadata );
        DRM_SHA_Finalize( &shadata, buffer );
    }
	else
	{
		/*data is  already hashed only copy it in to the correct value*/
		MEMCPY(buffer,f_pbData,f_cbData);
	}

    for( ntry = 0; ntry < 1000; ntry++ )
    {
        // BUGBUG: This should be the same logic as the original but I'm not sure if the logic is really perfect
        random_mod_nonzero( g_pkd.q, priv2, LNGQ, PBIGCTX_PASS );
        
        if( !ecaffine_exponentiation_tabular( g_pkd.TABLE, TABLE_SPACING, TABLE_LAST, priv2, LNGQ, Gpriv2, &g_pkd.ecurve, PBIGCTX_PASS ) )
        {
            ChkDR( DRM_E_PKCRYPTO_FAILURE );
        }

        if( ecaffine_is_infinite(Gpriv2, &g_pkd.ecurve, PBIGCTX_PASS ) )
        {
            break;
        }

        if( !FE2IPmod(Gpriv2, g_pkd.ecurve.fdesc, g_pkd.r, g_pkd.lngr, &g_pkd.rrecip, sigc, PBIGCTX_PASS ) )
        {
            break;
        }

        if( compare_immediate(gcd, 1, mp_gcdex(priv2, g_pkd.lngr, g_pkd.r, g_pkd.lngr,  priv2inv, digit_NULL, gcd, digit_NULL, &lgcd, NULL, PBIGCTX_PASS)) != 0 )
        {
            break;
        }

        multiply(sigc, g_pkd.lngr, privexpon, g_pkd.lngr, Gpriv2, PBIGCTX_PASS);   
        if( !byte_array_mod_bignum(buffer, gcd, PBIGCTX_PASS ) )
        {
            break;
        }
        
        add_diff(Gpriv2, 2*g_pkd.lngr, gcd, g_pkd.lngr, Gpriv2, digit_NULL, PBIGCTX_PASS);  /* Overflow impossible */
        divide(Gpriv2, 2*g_pkd.lngr, g_pkd.r, g_pkd.lngr, &g_pkd.rrecip, digit_NULL, gcd, PBIGCTX_PASS);
        multiply(priv2inv, g_pkd.lngr, gcd, g_pkd.lngr, Gpriv2, PBIGCTX_PASS);
        divide(Gpriv2, 2*g_pkd.lngr, g_pkd.r, g_pkd.lngr, &g_pkd.rrecip, digit_NULL, sigd, PBIGCTX_PASS);            
        
        /* TBD -- should check more error codes */

        if( !all_zero(sigc, g_pkd.lngr) 
         && !all_zero(sigd, g_pkd.lngr) )
        {
            break;
        }

    }

    mp_clear(priv2, g_pkd.lngr, PBIGCTX_PASS);    /* P1363 recommends this for security */
    mp_clear(priv2inv, g_pkd.lngr, PBIGCTX_PASS);
    mp_clear(Gpriv2, MAX(2*g_pkd.lngr, 2*g_pkd.ecurve.fdesc->elng), PBIGCTX_PASS);

    digits_to_dwords( sigc, (DWORD *)f_rgbSignature, LNGQDW, PBIGCTX_PASS );
    digits_to_dwords( sigd, ((DWORD *)f_rgbSignature) + LNGQDW, LNGQDW, PBIGCTX_PASS );

    endian_reverse_dwords( (const DWORD *)f_rgbSignature, (DWORD*) f_rgbSignature, 2*LNGQDW );

ErrorExit:
    PKUNLOCK;
    if( pContext )
    {
        if( gcd )
        {
            BignumSecureZeroMemory( gcd, (SIZEOF( digit_t) * (3*LNGQ + 3*MP_LONGEST) ) + (SIZEOF(ecaffine_t) * 2*MP_LONGEST ) + SIGN_BUF_LEN );
        }
        bignum_free( gcd, PBIGCTX_PASS );
    }
    return dr;
} /* end DRM_PK_Sign */


#endif // !DX_WMDRM_USE_CRYS

