/**@@@+++@@@@******************************************************************
**
** 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_COMMON.h"
#include "CRYS.h"

#ifndef WMDRM_ON_SEP

#ifdef DX_CC5_SEP_PLAT
/*The CRYS common function is not official CRYS API and the export private key is not implemented 
in the CC5 wrapping layer. In order to solve this problem these 2 functions were implmented in the WMDRM
wrapping layer */
extern void DRM_PK_ReverseMemcpy( DRM_BYTE *dst_ptr , DRM_BYTE *src_ptr , DRM_UINT size );
#define CRYS_COMMON_ReverseMemcpy  DRM_PK_ReverseMemcpy

extern DRM_RESULT DRM_PK_ExportPrivKey(CRYS_ECPKI_UserPrivKey_t  *UserPrivKey_ptr,
								DRM_BYTE		          PrivKeyOut[ __CB_DECL(PK_ENC_PRIVATE_KEY_LEN)]); 

#define CRYS_ECPKI_ExportPrivKey(DxPrivKey,WmDrmKey,size)  DRM_PK_ExportPrivKey(DxPrivKey,WmDrmKey);
#endif

/*********************************************************************
**
**  Function:  DRM_PK_GenKeyPair
**
**  Synopsis:  Generate a matched public and private key pair
**
**  Arguments:  
**     [f_pContext] -- Pointer to context the size of DRM_PKCRYPTO_CONTEXT_BUFFER_SIZE
**     [f_ppubkey]  -- New public key
**     [f_pprivkey] -- New private key
**
*********************************************************************/
DRM_RESULT DRM_API DRM_PK_GenKeyPair( 
    IN     DRM_VOID *f_pContext,
       OUT PUBKEY   *f_ppubkey,
       OUT PRIVKEY  *f_pprivkey )
{
    DRM_RESULT dr = DRM_E_PKCRYPTO_FAILURE;
	DRM_UINT  keySize;
	DRM_ECC_GLOBAL_UNION_t *EccBuff_ptr = (DRM_ECC_GLOBAL_UNION_t *)f_pContext;
    PUBKEY   pubkey;
    PRIVKEY  privkey;

    ChkArg( f_pContext != NULL 
         && f_ppubkey  != NULL 
         && f_pprivkey != NULL );
	/* generate key pair */
	dr = CRYS_ECPKI_GenKeyPair(CRYS_ECPKI_DomainID_WMDRM10,
							   &EccBuff_ptr->ECC_KG_stc.UserPrivKey,
							   &EccBuff_ptr->ECC_KG_stc.UserPublKey,
							   &EccBuff_ptr->ECC_KG_stc.TempData);

	if(dr != CRYS_OK)
	{
		goto ErrorExit;
	}
	
	keySize = sizeof(PRIVKEY);
	/* Extract the private key to WMDRM format */
	dr = CRYS_ECPKI_ExportPrivKey(&EccBuff_ptr->ECC_KG_stc.UserPrivKey, 
								  (DRM_BYTE*)&privkey.x,
								  &keySize);
	if(dr != CRYS_OK)
	{
		goto ErrorExit;
	}

	/* Chnage the byte order to WMDRM key format (The WMDRM work with littel endian while the CRYS export big endian) */
	CRYS_COMMON_ReverseMemcpy( f_pprivkey->x,(DRM_BYTE*)privkey.x , keySize );        

	keySize = sizeof(PUBKEY);

	/* Extract the public key to WMDRM format */
	dr = CRYS_ECPKI_ExportPublKey(&EccBuff_ptr->ECC_KG_stc.UserPublKey,
								  CRYS_EC_PointCompresOffMode,
								  (DRM_BYTE*)&pubkey.y,
								  (DxUint32_t*)&keySize);
	if(dr != CRYS_OK)
	{
		goto ErrorExit;
	}
	/* same as private key - change the endianess to the WMDRM internal format */
	CRYS_COMMON_ReverseMemcpy( f_ppubkey->y , (DRM_BYTE*)pubkey.y , keySize/2 );        

	CRYS_COMMON_ReverseMemcpy( (f_ppubkey->y + keySize/2) , (DRM_BYTE*)(pubkey.y + keySize/2) , keySize/2 );        

ErrorExit:
    return dr;    
}

#endif //WMDRM_ON_SEP

/*********************************************************************
**
**  Function:  DRM_PK_Encrypt
**
**  Synopsis:  Encrypt a block of data with a given public key.
**
**  Arguments:  
**     [f_pContext] -- Pointer to context the size of DRM_PKCRYPTO_CONTEXT_BUFFER_SIZE
**     [f_ppubkey]  -- Public key to encrypt with
**     [f_rgbIn]    -- Data to be encrypted
**     [f_rgbOut]   -- Buffer to hold the encrypted data value.
**
**  Notes:  Data is not encrpyted in place.  It is put in the rgbOut buffer.
*********************************************************************/
DRM_RESULT DRM_API DRM_PK_Encrypt( 
    IN        DRM_VOID *f_pContext, 
    IN  const PUBKEY   *f_ppubkey,
    IN  const DRM_BYTE  f_rgbIn[__CB_DECL(PK_ENC_PLAINTEXT_LEN)],
        OUT   DRM_BYTE  f_rgbOut[__CB_DECL(PK_ENC_CIPHERTEXT_LEN)] )
{
	DRM_RESULT dr = DRM_SUCCESS;
	DRM_UINT rgbOutLen = PK_ENC_CIPHERTEXT_LEN;
    DRM_BYTE  revrseRgbIn[__CB_DECL(PK_ENC_PLAINTEXT_LEN)];
    DRM_BYTE  tmpRgbOut[__CB_DECL(PK_ENC_CIPHERTEXT_LEN)];
	DRM_UINT  keySize;
	DRM_ECC_GLOBAL_UNION_t *EccBuff_ptr = (DRM_ECC_GLOBAL_UNION_t *)f_pContext;
	PUBKEY pubkey;
	
	ChkArg( f_pContext    != NULL
         && f_ppubkey     != NULL
         && f_rgbIn       != NULL
         && f_rgbOut      != NULL );

	keySize = sizeof(PUBKEY);

	/*Change the Key format to big endian as the CRYS expects */
	CRYS_COMMON_ReverseMemcpy( pubkey.y,(DRM_BYTE*)f_ppubkey->y , keySize/2 );
	CRYS_COMMON_ReverseMemcpy( (pubkey.y + keySize/2),(DRM_BYTE*)(f_ppubkey->y + keySize/2), keySize/2 );

	/* Build the CRYS public key from the WMDRM key format */
	dr = CRYS_ECPKI_BuildPublKeyFullCheck(CRYS_ECPKI_DomainID_WMDRM10,
										  pubkey.y,
										  keySize,
										  &EccBuff_ptr->ECC_BUILD_PUB_stc.UserPublKey,
										  &EccBuff_ptr->ECC_BUILD_PUB_stc.TempBuff);
	if(dr != CRYS_OK)
	{
		goto ErrorExit;
	}

	/* reverse the data order to big endian as the CRYS expects */
	CRYS_COMMON_ReverseMemcpy( revrseRgbIn,(DRM_BYTE*)f_rgbIn , PK_ENC_PLAINTEXT_LEN );        

	/*The public key is first in the build and encrypt structure so it can be used without conversion*/
	dr = CRYS_ECPKI_ELGAMAL_Encrypt (&EccBuff_ptr->ECC_ENC_stc.UserPublKey,
									 revrseRgbIn,
									 PK_ENC_PLAINTEXT_LEN,
									 tmpRgbOut,
									 (DxUint32_t*)&rgbOutLen,
									 &EccBuff_ptr->ECC_ENC_stc.TempData);

	if(dr != CRYS_OK)
	{
		goto ErrorExit;
	}

	/* reverse the bytes order from the CRYS format to the WMDRM format */
	CRYS_COMMON_ReverseMemcpy( f_rgbOut, tmpRgbOut , keySize/2 );        
	CRYS_COMMON_ReverseMemcpy( (f_rgbOut + keySize/2) , (tmpRgbOut + keySize/2) , keySize/2 );        
	CRYS_COMMON_ReverseMemcpy( (f_rgbOut + keySize) , (tmpRgbOut  + keySize) , keySize/2 );        
	CRYS_COMMON_ReverseMemcpy( (f_rgbOut + (keySize*3)/2) , (tmpRgbOut + (keySize*3)/2), keySize/2 );        


ErrorExit:
	return dr;
}



/*********************************************************************
**
**  Function:  DRM_PK_Decrypt
**
**  Synopsis:  
**
**  Arguments:  
**     [f_pContext] -- Pointer to context the size of DRM_PKCRYPTO_CONTEXT_BUFFER_SIZE
**     [f_pprivkey] -- Private key to decrypt with
**     [f_rgbIn]    -- Encrypted bytes that are to be decrypted
**     [f_rgbOut]   -- Clear text result
**
**  Notes:  Data is not decrpyted in place.  It is put in the rgbOut buffer.
**
*********************************************************************/
DRM_RESULT DRM_API DRM_PK_Decrypt(
    IN        DRM_VOID *f_pContext,
    IN  const PRIVKEY  *f_pprivkey,
    IN  const DRM_BYTE  f_rgbIn[__CB_DECL(PK_ENC_CIPHERTEXT_LEN)],
        OUT   DRM_BYTE  f_rgbOut[__CB_DECL(PK_ENC_PLAINTEXT_LEN)] )
{
    DRM_RESULT dr = DRM_SUCCESS;
	DRM_DWORD rgbOutLen = PK_ENC_PLAINTEXT_LEN;
	DRM_UINT  keySize;
    DRM_BYTE  reverseRgbIn[__CB_DECL(PK_ENC_CIPHERTEXT_LEN)];
    DRM_BYTE  tmpRgbOut[__CB_DECL(PK_ENC_PLAINTEXT_LEN)];
	DRM_ECC_GLOBAL_UNION_t *EccBuff_ptr = (DRM_ECC_GLOBAL_UNION_t *)f_pContext;
	PRIVKEY privkey;

    ChkArg( f_pContext    != NULL
         && f_pprivkey    != NULL 
         && f_rgbIn       != NULL 
         && f_rgbOut      != NULL );

	keySize = sizeof(PRIVKEY);

	/*Change the Key format to big endian as the CRYS expects */
	CRYS_COMMON_ReverseMemcpy( privkey.x,(DRM_BYTE*)f_pprivkey->x, keySize );        

	/* Build the CRYS private key from the WMDRM key format */
	dr = CRYS_ECPKI_BuildPrivKey(CRYS_ECPKI_DomainID_WMDRM10,
								 privkey.x,
								 keySize,
								 &EccBuff_ptr->ECC_DEC_stc.UserPrivKey);
	if(dr != CRYS_OK)
	{
		goto ErrorExit;
	}
	
	/* reverse the bytes order from the WMDRM format to the CRYS format */
	CRYS_COMMON_ReverseMemcpy( reverseRgbIn, (DRM_BYTE*)f_rgbIn , keySize );        
	CRYS_COMMON_ReverseMemcpy( (reverseRgbIn + keySize), (DRM_BYTE*)(f_rgbIn + keySize) , keySize );        
	CRYS_COMMON_ReverseMemcpy( (reverseRgbIn + 2*keySize), (DRM_BYTE*)(f_rgbIn  + 2*keySize), keySize );        
	CRYS_COMMON_ReverseMemcpy( (reverseRgbIn + 3*keySize), (DRM_BYTE*)(f_rgbIn + 3*keySize), keySize );        

	/* Decrypt the data */
	dr = CRYS_ECPKI_ELGAMAL_Decrypt (&EccBuff_ptr->ECC_DEC_stc.UserPrivKey,
									 reverseRgbIn,
									 PK_ENC_CIPHERTEXT_LEN,
									 tmpRgbOut,
									 (DxUint32_t*)&rgbOutLen,
									 &EccBuff_ptr->ECC_DEC_stc.TempData);

	if(dr != CRYS_OK)
	{
		goto ErrorExit;
	}
	/* convert the data to WMDRM format */
	CRYS_COMMON_ReverseMemcpy( f_rgbOut, tmpRgbOut, PK_ENC_PLAINTEXT_LEN );        

ErrorExit:

	return( dr );
}



/*********************************************************************
**
**  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(
    IN       DRM_VOID  *f_pContext,
    IN const PRIVKEY   *f_privkey,
    IN const DRM_BYTE  *f_pbData,
    IN       DRM_DWORD  f_cbData,
       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 HashResultBuff ;
	CRYS_HASH_Result_t reverseHashResultBuff ;
	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_cbData       != 0 
         && 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;
	}
	/* calculate the hash result */
	dr = CRYS_HASH(CRYS_HASH_SHA1_mode,
				   (DRM_BYTE*)f_pbData,
				   f_cbData,
				   HashResultBuff);

	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*)reverseHashResultBuff,(DxUint8_t*)HashResultBuff, 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*)reverseHashResultBuff,
						 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;
} /* end DRM_PK_Sign */


/*********************************************************************
**
**  Function:  DRM_PK_Verify
**
**  Synopsis:  Verify a digital signature created by DRM_PK_Sign.
**
**  Arguments:  
**     [f_pContext]     -- Pointer to context the size of DRM_PKCRYPTO_CONTEXT_BUFFER_SIZE
**     [f_ppubkey]      -- Pubkey to check the signature with
**     [f_pbData]       -- Data buffer that the signature was created over
**     [f_cbData]       -- Length of pbBuffer in bytes
**     [f_rgbSignature] -- The signature to verify
**
**  Returns:  TRUE if the signature verified correctly.  FALSE is it didn't
**
*********************************************************************/
DRM_BOOL DRM_API DRM_PK_Verify( 
    IN       DRM_VOID  *f_pContext,
    IN const PUBKEY    *f_ppubkey, 
    IN const DRM_BYTE  *f_pbData, 
    IN       DRM_DWORD  f_cbData, 
    IN const DRM_BYTE   f_rgbSignature[__CB_DECL(PK_ENC_SIGNATURE_LEN)] )
{    
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_BYTE  revrseSignature[__CB_DECL(PK_ENC_SIGNATURE_LEN)];
	CRYS_HASH_Result_t HashResultBuff ;
	CRYS_HASH_Result_t reverseHashResultBuff ;
	DRM_UINT  keySize;
	DRM_ECC_GLOBAL_UNION_t *EccBuff_ptr = (DRM_ECC_GLOBAL_UNION_t *)f_pContext;
	PUBKEY pubkey;

    ChkArg( f_pContext     != NULL 
         && f_ppubkey      != NULL 
         && f_pbData       != NULL 
         && f_cbData       != 0 
         && f_rgbSignature != NULL );

	keySize = sizeof(PUBKEY);
	/*reverse the key to match CRYS format*/
	CRYS_COMMON_ReverseMemcpy( pubkey.y,(DRM_BYTE*)f_ppubkey->y , keySize/2 );        
	CRYS_COMMON_ReverseMemcpy( (pubkey.y + keySize/2) , (DRM_BYTE*)(f_ppubkey->y + keySize/2), keySize/2 );        

	/*reverse the signature to CRYS format*/
	CRYS_COMMON_ReverseMemcpy( revrseSignature,(DRM_BYTE*)f_rgbSignature , PK_ENC_SIGNATURE_LEN );        

	/* build the public key in CRYS format */
	dr = CRYS_ECPKI_BuildPublKeyFullCheck(CRYS_ECPKI_DomainID_WMDRM10,
										  pubkey.y,
										  keySize,
										  &EccBuff_ptr->ECC_BUILD_PUB_stc.UserPublKey,
										  &EccBuff_ptr->ECC_BUILD_PUB_stc.TempBuff);
	if(dr != CRYS_OK)
	{
		dr = DRM_E_PKCRYPTO_FAILURE;
		goto ErrorExit;
	}
	
	/* build the hash result */
	dr = CRYS_HASH(CRYS_HASH_SHA1_mode,
				   (DRM_BYTE*)f_pbData,
				   f_cbData,
				   HashResultBuff);

	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*)reverseHashResultBuff,(DxUint8_t*)HashResultBuff, SHA_DIGEST_LEN );        

	/* Verify the signature */
	dr = CRYS_ECDSA_Verify (&EccBuff_ptr->ECC_VERIFY_stc.VerifyUserContext,
							&EccBuff_ptr->ECC_VERIFY_stc.UserPublKey,
							CRYS_ECPKI_AFTER_HASH_SHA1_mode,
							(DRM_BYTE*)revrseSignature,
							PK_ENC_SIGNATURE_LEN,
							(DRM_BYTE*)reverseHashResultBuff,
							SHA_DIGEST_LEN);

	if(dr != CRYS_OK)
	{
		dr = DRM_E_PKCRYPTO_FAILURE;
		goto ErrorExit;
	}
ErrorExit:
	return DRM_SUCCEEDED( dr );
} /* end ecvp_dsa */

#else
#ifndef WMDRM_ON_SEP
#include <time.h>
#endif
#include <assert.h>
#include <limits.h>
#include <field.h>
#include <ecurve.h>
#include <mprand.h>
#include <stdio.h>
#include <drmcommon.h>
#include <drmprofile.h>
#include <drmpkcrypto.h>
#include <drmsha1.h>
#include <string.h>
#include <bignum.h>

#define USE_REAL_MEMORY_ALLOCATION 0

#if USE_REAL_MEMORY_ALLOCATION
#ifdef __cplusplus
extern "C" {
#endif
DWORD cbMax = 0;
DWORD cbCur = 0;
#ifdef __cplusplus
}
#endif
#endif


typedef struct __DRMBIGNUM_CONTEXT_STRUCT
{
    DRM_STACK_ALLOCATOR_CONTEXT oHeap;
    DRM_BYTE rgbHeap[__CB_DECL(DRM_PKCRYPTO_CONTEXT_BUFFER_INTERNAL_SIZE)];
}DRMBIGNUM_CONTEXT_STRUCT;

#define LNGQDW 5
#define LNGQ DWORDS_TO_DIGITS(LNGQDW)
#define QBITS (LNGQDW * DWORD_BITS)

static const DWORD ecdw_q[5] = 
{
    0x141424F7, 0x31415926, 0x27182818, 0x01234567, 0x89ABCDEF
};
/* ecdw_q = 785963102379428822376694789446897396207498568951 */

static const DWORD ecdw_order[5] = 
{
    0x28C2A675, 0xEC149044, 0x2716B26E, 0x01234567, 0x89ABCDEF
};
/* ecdw_order = 785963102379428822376693024881714957612686157429 */

static const DWORD ecdw_a[5] = 
{
    0xEBE41497, 0x4780C009, 0x7632FF3D, 0xD277BCE8, 0x37A5ABCC
};
/* ecdw_a = 317689081251325503476317476413827693272746955927 */

static const DWORD ecdw_b[5] = 
{
    0x9328239E, 0xD78FDEDF, 0x28E85F1A, 0x725E2F32, 0x0DD8DABF
};
/* ecdw_b = 79052896607878758718120572025718535432100651934 */

static const DWORD ecdw_genx[5] = 
{
    0x109FA120, 0xBA38DAF0, 0x3510C07D, 0xD6A3A1E5, 0x8723947F
};
/* ecdw_genx = 771507216262649826170648268565579889907769254176 */

static const DWORD ecdw_geny[5] = 
{
    0xA379936F, 0xD4ED7ACD, 0x8C3C5856, 0x1075522D, 0x44574491
};
/* ecdw_geny = 390157510246556628525279459266514995562533196655 */


static void endian_reverse_dwords( const DWORD f_rgdwIn[], DWORD f_rgdwOut[], DWORD cdw )
{
    if( f_rgdwOut != f_rgdwIn )
    {
        MEMCPY( f_rgdwOut, f_rgdwIn, cdw*SIZEOF(DWORD) );
    }

#if !TARGET_LITTLE_ENDIAN
    while( cdw>0 )
    {
        DWORD dw;

        cdw--;
        BYTES_TO_DWORD( dw, (DRM_BYTE*) &f_rgdwOut[cdw] );
        FIX_ENDIAN_DWORD( dw );
        DWORD_TO_BYTES( (DRM_BYTE*) &f_rgdwOut[cdw], dw );
    }
#endif

}

static BOOL dw_to_modular(               
    DWORDC        *dwnum,
    DWORDC         lngdw,
    digit_t       *answer,      /* OUT, length LNGQ */
    mp_modulus_tc *modulo,
    PBIGCTX_ARG    )
{
    digit_t *temp1;
    BOOL OK = FALSE;

    temp1 = (digit_t *) bignum_alloc (SIZEOF (digit_t) * LNGQ, PBIGCTX_PASS);
    if( temp1 != NULL )
    {

        if (DWORDS_TO_DIGITS(lngdw) <= LNGQ)
        {        
            dwords_to_digits(dwnum, temp1, lngdw, PBIGCTX_PASS);
            OK = to_modular(temp1, DWORDS_TO_DIGITS(lngdw), answer, modulo, PBIGCTX_PASS);
        }

        BignumSecureZeroMemory( temp1, SIZEOF (digit_t) * LNGQ );
        bignum_free (temp1, PBIGCTX_PASS);
    }

    return OK;
} /* end dw_to_modular */


BOOL words_to_ecaffine( DWORDC *in, ecaffine_t *P,        /* OUT */
                       ecurve_tc  *E,
                       PBIGCTX_ARG)  
                       /* map bits of "in" to a point on the curve such that the first n-1 bytes can be recovered

                          NOTE: this is only supposed to work for my ec over FIELD_Q_MP and not intended to be
                                used as a general purpose library function.    marcuspe

                         NOTE -- We never return the point at infinity,
                                 so this may fail if the group has no other points,
                                 such as y^2 == x^3 - x - 1 (mod 3)
                         NOTE -- A given point of order 2 (with y = 0) is twice as
                                 likely to be chosen as a given point of order > 2.
                       */
{
    BOOL found = FALSE;
    INT tries;    
    flongest_t *t1;
    DWORD *tmp; /*[LNGQDW] */

    tmp = (DWORD*) bignum_alloc (SIZEOF (DWORD) * LNGQDW + SIZEOF (flongest_t), PBIGCTX_PASS);
    if( tmp == NULL )
    {
        return found;
    }

    t1 = (flongest_t*)(tmp + LNGQDW);
    mp_copy( in, tmp, LNGQDW-1, PBIGCTX_PASS );
    switch(E->fdesc->ftype) {
    case FIELD_Q_MP:
    case FIELD_2_POLYNOMIAL: 
    case FIELD_2_NORMAL:
        for (tries = 0; 
            tries < 100 && !found;
            tries++) {

                tmp[LNGQDW-1] = tries; 
                if( !dw_to_modular( tmp, LNGQDW, P, E->fdesc->modulo, PBIGCTX_PASS) )
                {
                    found = FALSE;
                    break;
                }
                if (E->fdesc->ftype == FIELD_Q_MP || Kiszero(P, E->fdesc, PBIGCTX_PASS) )
                {
                    if (Kmuladd (P, P,   E->a, *t1, E->fdesc, digit_NULL, PBIGCTX_PASS) 
                    &&  Kmuladd (P, *t1, E->b, *t1, E->fdesc, digit_NULL, PBIGCTX_PASS))
                    /*      t1 = x^2 + a                      t1 = x^3 + ax + b */
                    {
                        /* fSquare must be volatile to work in optimized builds of some compilers */
                        volatile BOOL fSquare = FALSE;
                        found = Kprime_sqrter(*t1, (P + E->fdesc->elng), 1, E->fdesc, ( BOOL* )&fSquare, PBIGCTX_PASS) && fSquare;/* Is x^3 + ax + b  a square? */
                        continue;
                    }
                    found = FALSE;
                    break;
                } 
                else 
                {   
                    BOOL fSuccess = FALSE;
                    /* 
                       Characteristic 2, P nonzero
                       Solve y^2 + x*y = x^3 + a*x^2 + b for y.
                       This is equivalent to (y/x)^2 + (y/x) = x + a + b/x^2
                    */
                    if (Kmul     (P,     P, *t1, E->fdesc, digit_NULL, PBIGCTX_PASS) 
                    &&  Kdiv     (E->b, *t1, *t1,      E->fdesc, digit_NULL, PBIGCTX_PASS) 
                    &&  Kadd     (*t1,   P, *t1, E->fdesc, PBIGCTX_PASS) 
                    &&  Kadd     (*t1,   E->a, *t1, E->fdesc, PBIGCTX_PASS) 
                    &&  K2_quad1 (*t1, (P + E->fdesc->elng), E->fdesc, &fSuccess, PBIGCTX_PASS) 
                    &&  fSuccess
                    &&  Kmul     (P, (P + E->fdesc->elng), (P + E->fdesc->elng), E->fdesc, digit_NULL, PBIGCTX_PASS) ) 
                    {
                        found = TRUE;
                        continue;
                    }
                }
            } /* for tries */
            break;
    } /* switch */

    BignumSecureZeroMemory( tmp, SIZEOF (DWORD) * LNGQDW + SIZEOF (flongest_t) );
    bignum_free (tmp, PBIGCTX_PASS);
    return found;
} /* words_to_ecaffine */


static BOOL ecaffine_to_dwords( ecaffine_t *P, DWORD *out, ecurve_tc  *E, PBIGCTX_ARG)
{
    /* inverts the mapping of dwords_to_ecaffine */
    DWORD *tmp; /*[LNGQDW]; */
    digit_t *digs; /*[LNGQ]; */

    tmp =(DWORD*) bignum_alloc( SIZEOF(DWORD) * LNGQDW + SIZEOF( digit_t ) * LNGQ, PBIGCTX_PASS);
    if( tmp != NULL )
    {
    
        digs = (digit_t*)(tmp + LNGQDW );
        from_modular ( P, digs, E->fdesc->modulo, PBIGCTX_PASS);
        digits_to_dwords( digs, tmp, LNGQDW, PBIGCTX_PASS );
        endian_reverse_dwords( (const DWORD *)tmp, out, LNGQDW-1 );
        
        BignumSecureZeroMemory( tmp, SIZEOF(DWORD) * LNGQDW + SIZEOF( digit_t ) * LNGQ );
        bignum_free (tmp, PBIGCTX_PASS);
        return( TRUE );
    }

    return FALSE;
}


#define TABLE_SPACING 4
#define TABLE_LAST 60


#if USE_REAL_MEMORY_ALLOCATION
#define PK_DATA_HEAP_SIZE 1
#else
#define PK_DATA_HEAP_SIZE 250
#endif
typedef struct {
    digit_t q[LNGQ]; 
    digit_t a[LNGQ];
    digit_t b[LNGQ];
    digit_t r[LNGQ];         /* from singature file,   order of g */
    DWORD   lngr;            /* Length of r (digits) */

    ecaffine_t g[2*LNGQ];
    ecaffine_t TABLE[ 2 * LNGQ * (TABLE_LAST+1) ];  /* TEST */

    mp_modulus_t   qmodulus;
    ecurve_t       ecurve;
    field_desc_t   fdesc;    
    reciprocal_1_t rrecip;             /* For division by r */
    
    DRM_STACK_ALLOCATOR_CONTEXT pkdHeap;
    BYTE rgbHeap[__CB_DECL(PK_DATA_HEAP_SIZE)];
} PK_DATA;


#ifndef WMDRM_ON_SEP
static PK_DATA g_pkd;
#else
PK_DATA g_pkd;
#endif
static DRM_RESULT DRM_API _ThreadUnSafePKInit( DRM_VOID* pContext );
DRM_RESULT DRM_API _ThreadSafeInit( DRM_VOID* pContext );
static DRM_VOID _PKLock();
static DRM_VOID _PKUnlock();

#if DRM_THREAD_SAFE
#define USEPKLOCK DRM_BOOL fLocked = FALSE;
#define PKLOCK {_PKLock();fLocked=TRUE;}
#define PKUNLOCK {if(fLocked){_PKUnlock();}}
#define PKUNINIT {_PKUnInit();}
#else
#define USEPKLOCK
#define PKLOCK
#define PKUNLOCK
#define PKUNINIT
#endif

/*
**  All DRM_PK_MAX_SECRET_ARRAYS pointers are initialized to NULL.
*/
PK_SET_SECRET_FUNCS g_SetSecretFuncs = 
{
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
};

static HRESULT ec_init( PK_DATA *pEc, 
                       const DWORD *q, 
                       const DWORD *order, 
                       const DWORD *a, 
                       const DWORD *b, 
                       const DWORD *genx, 
                       const DWORD *geny, 
                       PBIGCTX_ARG) 
{
    DRM_DWORD i =0;
    DRM_RESULT dr = DRM_SUCCESS;

    ChkArg( pEc   != NULL 
         || q     != NULL 
         || order != NULL 
         || a     != NULL 
         || b     != NULL 
         || genx  != NULL 
         || geny  != NULL );
    
    dwords_to_digits( q, pEc->q, LNGQDW, PBIGCTX_PASS );
    dwords_to_digits( order, pEc->r, LNGQDW, PBIGCTX_PASS );

    ChkBOOL( create_modulus( pEc->q, LNGQ, FROM_RIGHT, &pEc->qmodulus, PBIGCTX_PASS, (struct bigctx_t*)&g_pkd.pkdHeap ), DRM_E_PKCRYPTO_FAILURE);
    ChkBOOL( dw_to_modular( a, LNGQDW, pEc->a, &pEc->qmodulus, PBIGCTX_PASS ), DRM_E_PKCRYPTO_FAILURE);
    ChkBOOL( dw_to_modular( b, LNGQDW, pEc->b, &pEc->qmodulus, PBIGCTX_PASS ), DRM_E_PKCRYPTO_FAILURE);
    ChkBOOL( Kinitialize_prime( &pEc->qmodulus, &pEc->fdesc, PBIGCTX_PASS , (struct bigctx_t*)&g_pkd.pkdHeap ), DRM_E_PKCRYPTO_FAILURE);
    ChkBOOL( ec_initialize( pEc->a, pEc->b, &pEc->fdesc, &pEc->ecurve, PBIGCTX_PASS, (struct bigctx_t*)&g_pkd.pkdHeap ), DRM_E_PKCRYPTO_FAILURE);
    ChkBOOL( dw_to_modular( ecdw_genx, LNGQDW, pEc->g, &pEc->qmodulus, PBIGCTX_PASS ), DRM_E_PKCRYPTO_FAILURE);
    ChkBOOL( dw_to_modular( ecdw_geny, LNGQDW, pEc->g + LNGQ, &pEc->qmodulus, PBIGCTX_PASS ), DRM_E_PKCRYPTO_FAILURE);
    ChkBOOL( ecaffine_on_curve(pEc->g, &pEc->ecurve, NULL, digit_NULL, PBIGCTX_PASS), DRM_E_PKCRYPTO_FAILURE);
    ChkBOOL( ecaffine_table_construction( pEc->g, TABLE_SPACING, TABLE_LAST, pEc->TABLE, &pEc->ecurve, PBIGCTX_PASS ), DRM_E_PKCRYPTO_FAILURE);

    for( i = 0; i < DRM_PK_MAX_SECRET_ARRAYS; i++ )
    {
        if( (*(g_SetSecretFuncs.secretArray[i]))       != NULL
         && (*(g_SetSecretFuncs.secretArray[i]))[6]    != NULL
         && (*(g_SetSecretFuncs.secretArray[i]))[6]( ) != DRM_SUCCESS )
        {
            ChkDR( DRM_E_PKCRYPTO_FAILURE );
        }
        if( (*(g_SetSecretFuncs.secretArray[i])) != NULL )
        {
            (*(g_SetSecretFuncs.secretArray[i]))[6] = NULL;
        }
    }

    pEc->lngr = LNGQ;
    divide_precondition_1( pEc->r, pEc->lngr, &pEc->rrecip, PBIGCTX_PASS );

ErrorExit:
    return dr;
}


static DRM_RESULT DRM_API _ThreadUnSafePKInit( DRM_VOID* f_pContext )
{
    static DRM_BOOL fInited = FALSE;
    DRM_RESULT dr = DRM_E_PKCRYPTO_FAILURE;
    DRM_DWORD i =0;
    DRMBIGNUM_CONTEXT_STRUCT *pContext = (DRMBIGNUM_CONTEXT_STRUCT*) f_pContext;

    DRMCASSERT( SIZEOF( DRMBIGNUM_CONTEXT_STRUCT ) == DRM_PKCRYPTO_CONTEXT_BUFFER_SIZE );    

    if( fInited )
    {
        return DRM_SUCCESS;
    }
    
    MEMSET( (DRM_BYTE*)&g_pkd, 0, SIZEOF( g_pkd ));    

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

    mp_initialization((struct bigctx_t*)&pContext->oHeap);

    g_pkd.pkdHeap.nStackTop = 0;
    g_pkd.pkdHeap.cbStack = PK_DATA_HEAP_SIZE;
    g_pkd.pkdHeap.pbStack = g_pkd.rgbHeap;

    for( i = 0; i < DRM_PK_MAX_SECRET_ARRAYS; i++ )
    {
        if( (*(g_SetSecretFuncs.secretArray[i]))       != NULL
         && (*(g_SetSecretFuncs.secretArray[i]))[0]    != NULL
         && (*(g_SetSecretFuncs.secretArray[i]))[0]( ) != DRM_SUCCESS )
        {
            return DRM_E_PKCRYPTO_FAILURE;
        }
        if( (*(g_SetSecretFuncs.secretArray[i])) != NULL )
        {
            (*(g_SetSecretFuncs.secretArray[i]))[0] = NULL;
        }
    }

    dr = ec_init(&g_pkd, ecdw_q, ecdw_order, ecdw_a, ecdw_b, ecdw_genx, ecdw_geny, (struct bigctx_t*)&pContext->oHeap);
    if( DRM_SUCCEEDED( dr ) )
    {
        fInited = TRUE;
    }
    return dr;
}

#if DRM_THREAD_SAFE
#define PKInit( pContext ) _ThreadSafeInit( pContext )
#else
#define PKInit( pContext ) _ThreadUnSafePKInit( pContext )
#endif
#ifndef WMDRM_ON_SEP
/*********************************************************************
**
**  Function:  DRM_PK_GenKeyPair
**
**  Synopsis:  Generate a matched public and private key pair
**
**  Arguments:  
**     [f_pContext] -- Pointer to context the size of DRM_PKCRYPTO_CONTEXT_BUFFER_SIZE
**     [f_ppubkey]  -- New public key
**     [f_pprivkey] -- New private key
**
*********************************************************************/
DRM_RESULT DRM_API DRM_PK_GenKeyPair( 
    IN     DRM_VOID *f_pContext,
       OUT PUBKEY   *f_ppubkey,
       OUT PRIVKEY  *f_pprivkey )
{
    HRESULT dr = DRM_E_PKCRYPTO_FAILURE;
    DRMBIGNUM_CONTEXT_STRUCT *pContext = (DRMBIGNUM_CONTEXT_STRUCT *)f_pContext;
    PBIGCTX_ARG         = NULL;
    digit_t    *tmp     = NULL; /* [LNGQ] */
    digit_t    *priv    = NULL; /* [LNGQ] */
    ecaffine_t *pub     = NULL; /* [2*LNGQ] */
    USEPKLOCK;

    ChkArg( f_pContext != NULL 
         && f_ppubkey  != NULL 
         && f_pprivkey != 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( tmp  = (digit_t*) bignum_alloc( (SIZEOF ( digit_t ) * 2 * LNGQ) + ( SIZEOF( ecaffine_t ) * 2 *LNGQ ), PBIGCTX_PASS) );

    priv = (digit_t*)   (tmp  + LNGQ);
    pub  = (ecaffine_t*)(priv + LNGQ);
    
    random_mod_nonzero( g_pkd.q, priv, LNGQ, PBIGCTX_PASS );
    if( ecaffine_exponentiation_tabular( g_pkd.TABLE, TABLE_SPACING, TABLE_LAST, priv, LNGQ, pub, &(g_pkd.ecurve), PBIGCTX_PASS ) )
    {

        digits_to_dwords( priv, (DWORD*)f_pprivkey->x, LNGQDW, PBIGCTX_PASS );
        endian_reverse_dwords( (DWORD*)f_pprivkey->x, (DWORD*)f_pprivkey->x, PK_ENC_PRIVATE_KEY_LEN / SIZEOF( DWORD ) );
        
        from_modular ( pub, tmp, &(g_pkd.qmodulus), PBIGCTX_PASS );
        digits_to_dwords( tmp, (DWORD*)f_ppubkey->y, LNGQDW, PBIGCTX_PASS ); 

        from_modular ( pub+LNGQ, tmp, &(g_pkd.qmodulus), PBIGCTX_PASS );
        digits_to_dwords( tmp, ((DWORD*)f_ppubkey->y) + LNGQDW, LNGQDW, PBIGCTX_PASS ); 
        endian_reverse_dwords( (DWORD*)f_ppubkey->y, (DWORD *)f_ppubkey->y, PK_ENC_PUBLIC_KEY_LEN / SIZEOF( DWORD ) );

        dr = DRM_SUCCESS;
    }

    BignumSecureZeroMemory( tmp, (SIZEOF ( digit_t ) * 2 * LNGQ) + ( SIZEOF( ecaffine_t ) * 2 *LNGQ ) );
    bignum_free( tmp, PBIGCTX_PASS );

ErrorExit:
    PKUNLOCK;
    return dr;    
}

#endif //WMDRM_ON_SEP
/*********************************************************************
**
**  Function:  DRM_PK_Encrypt
**
**  Synopsis:  Encrypt a block of data with a given public key.
**
**  Arguments:  
**     [f_pContext] -- Pointer to context the size of DRM_PKCRYPTO_CONTEXT_BUFFER_SIZE
**     [f_ppubkey]  -- Public key to encrypt with
**     [f_rgbIn]    -- Data to be encrypted
**     [f_rgbOut]   -- Buffer to hold the encrypted data value.
**
**  Notes:  Data is not encrpyted in place.  It is put in the rgbOut buffer.
*********************************************************************/
DRM_RESULT DRM_API DRM_PK_Encrypt( 
    IN        DRM_VOID *f_pContext, 
    IN  const PUBKEY   *f_ppubkey,
    IN  const DRM_BYTE  f_rgbIn[__CB_DECL(PK_ENC_PLAINTEXT_LEN)],
        OUT   DRM_BYTE  f_rgbOut[__CB_DECL(PK_ENC_CIPHERTEXT_LEN)] )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRMBIGNUM_CONTEXT_STRUCT *pContext = (DRMBIGNUM_CONTEXT_STRUCT *)f_pContext;
    DWORD rgdwPubKey[ PK_ENC_PUBLIC_KEY_LEN / SIZEOF( DWORD )  ];
    PBIGCTX_ARG     = NULL;
    digit_t    *k   = NULL; /* [LNGQ]   */
    ecaffine_t *a   = NULL; /* [2*LNGQ] */
    ecaffine_t *b   = NULL; /* [2*LNGQ] */
    ecaffine_t *tmp = NULL; /* [2*LNGQ] */
    ecaffine_t *ecM = NULL; /* [2*LNGQ] */
    ecaffine_t *pk  = NULL; /* [2*LNGQ] */
    USEPKLOCK;

    ChkArg( f_pContext    != NULL
         && f_ppubkey     != NULL
         && f_rgbIn       != NULL
         && f_rgbOut      != 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( k = (digit_t*) bignum_alloc( (SIZEOF( digit_t) * LNGQ ) + ( SIZEOF (ecaffine_t) * 5*2*LNGQ ), PBIGCTX_PASS ) ); 

    a   = (ecaffine_t*)(k   + LNGQ);
    b   = (ecaffine_t*)(a   + 2 * LNGQ);
    tmp = (ecaffine_t*)(b   + 2 * LNGQ);
    ecM = (ecaffine_t*)(tmp + 2 * LNGQ);
    pk  = (ecaffine_t*)(ecM + 2 * LNGQ);

    endian_reverse_dwords( (const DWORD*)f_ppubkey->y, rgdwPubKey, PK_ENC_PUBLIC_KEY_LEN / SIZEOF( DWORD ) );

    dw_to_modular( rgdwPubKey,          LNGQDW, pk,      &(g_pkd.qmodulus), PBIGCTX_PASS );
    dw_to_modular( rgdwPubKey + LNGQDW, LNGQDW, pk+LNGQ, &(g_pkd.qmodulus), PBIGCTX_PASS );

    if( !ecaffine_on_curve(pk, &(g_pkd.ecurve), NULL, digit_NULL, PBIGCTX_PASS ) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }
    
    endian_reverse_dwords( (const DWORD*)f_rgbIn, (DWORD*)f_rgbOut, PK_ENC_PLAINTEXT_LEN / SIZEOF(DRM_DWORD) );
    if( !words_to_ecaffine( (DWORD *)f_rgbOut, ecM, &(g_pkd.ecurve), PBIGCTX_PASS ) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }

    random_mod_nonzero( g_pkd.q, k, LNGQ, PBIGCTX_PASS );

    if( !ecaffine_exponentiation_tabular( g_pkd.TABLE, TABLE_SPACING, TABLE_LAST, k, LNGQ, a, &(g_pkd.ecurve), PBIGCTX_PASS ) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }

    if( !ecaffine_on_curve( pk, &g_pkd.ecurve, NULL, digit_NULL, PBIGCTX_PASS ) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }

    if( !ecaffine_exponentiation_tabular( pk, TABLE_SPACING, 0, k, LNGQ, tmp, &(g_pkd.ecurve), PBIGCTX_PASS ) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }

    if( !ecaffine_addition( tmp, ecM, b, 1, &(g_pkd.ecurve), digit_NULL, PBIGCTX_PASS  ) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }


    from_modular ( a, tmp, &(g_pkd.qmodulus), PBIGCTX_PASS );
    digits_to_dwords( tmp, (DWORD *)f_rgbOut, LNGQDW, PBIGCTX_PASS ); 
    from_modular ( a+LNGQ, tmp, &g_pkd.qmodulus, PBIGCTX_PASS );
    digits_to_dwords( tmp, ((DWORD *)f_rgbOut)+LNGQDW, LNGQDW, PBIGCTX_PASS ); 

    from_modular ( b, tmp, &g_pkd.qmodulus, PBIGCTX_PASS );
    digits_to_dwords( tmp, ((DWORD *)f_rgbOut) + 2*LNGQDW, LNGQDW, PBIGCTX_PASS ); 
    from_modular ( b+LNGQ, tmp, &g_pkd.qmodulus, PBIGCTX_PASS );
    digits_to_dwords( tmp, ((DWORD *)f_rgbOut) + 3*LNGQDW, LNGQDW, PBIGCTX_PASS ); 

    endian_reverse_dwords( (DWORD *)f_rgbOut, (DWORD*)f_rgbOut, 4*LNGQDW );

ErrorExit:
    PKUNLOCK;
    if( k )
    {
        BignumSecureZeroMemory( k, (SIZEOF( digit_t) * LNGQ ) + ( SIZEOF (ecaffine_t) * 5*2*LNGQ ) );
    }
    bignum_free( k, PBIGCTX_PASS );
    return dr;
}


/*********************************************************************
**
**  Function:  DRM_PK_Decrypt
**
**  Synopsis:  
**
**  Arguments:  
**     [f_pContext] -- Pointer to context the size of DRM_PKCRYPTO_CONTEXT_BUFFER_SIZE
**     [f_pprivkey] -- Private key to decrypt with
**     [f_rgbIn]    -- Encrypted bytes that are to be decrypted
**     [f_rgbOut]   -- Clear text result
**
**  Notes:  Data is not decrpyted in place.  It is put in the rgbOut buffer.
**
*********************************************************************/
DRM_RESULT DRM_API DRM_PK_Decrypt(
    IN        DRM_VOID *f_pContext,
    IN  const PRIVKEY  *f_pprivkey,
    IN  const DRM_BYTE  f_rgbIn[__CB_DECL(PK_ENC_CIPHERTEXT_LEN)],
        OUT   DRM_BYTE  f_rgbOut[__CB_DECL(PK_ENC_PLAINTEXT_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 rgdwIn     [PK_ENC_CIPHERTEXT_LEN / SIZEOF( DWORD )];
    ecaffine_t *a   = NULL; /* [2*LNGQ] */
    ecaffine_t *b   = NULL; /* [2*LNGQ] */
    ecaffine_t *tmp = NULL; /* [2*LNGQ] */
    ecaffine_t *ecM = NULL; /* [2*LNGQ] */
    digit_t    *pk  = NULL; /* [LNGQ]   */
    PBIGCTX_ARG     = NULL;
    USEPKLOCK;
    
    ChkArg( f_pContext    != NULL
         && f_pprivkey    != NULL 
         && f_rgbIn       != NULL 
         && f_rgbOut      != 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( pk = (digit_t*)bignum_alloc( (SIZEOF( digit_t) * LNGQ ) + ( SIZEOF (ecaffine_t) * 4*2*LNGQ ), PBIGCTX_PASS ) );

    a   = (ecaffine_t*)(pk  +     LNGQ);
    b   = (ecaffine_t*)(a   + 2 * LNGQ);
    tmp = (ecaffine_t*)(b   + 2 * LNGQ);
    ecM = (ecaffine_t*)(tmp + 2 * LNGQ);


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

    endian_reverse_dwords( (const DWORD *)f_rgbIn, rgdwIn, PK_ENC_CIPHERTEXT_LEN / SIZEOF(DWORD) );

    dw_to_modular( rgdwIn,          LNGQDW, a,      &g_pkd.qmodulus, PBIGCTX_PASS );
    dw_to_modular( rgdwIn + LNGQDW, LNGQDW, a+LNGQ, &g_pkd.qmodulus, PBIGCTX_PASS );
    dw_to_modular( rgdwIn+2*LNGQDW, LNGQDW, b,      &g_pkd.qmodulus, PBIGCTX_PASS );
    dw_to_modular( rgdwIn+3*LNGQDW, LNGQDW, b+LNGQ, &g_pkd.qmodulus, PBIGCTX_PASS );

    if( !ecaffine_on_curve( a, &g_pkd.ecurve, NULL, digit_NULL, PBIGCTX_PASS ) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }

    if( !ecaffine_exponentiation_tabular( a, TABLE_SPACING, 0, pk, LNGQ, tmp, &(g_pkd.ecurve), PBIGCTX_PASS ) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }
    if( !ecaffine_addition( b, tmp, ecM, -1, &(g_pkd.ecurve), digit_NULL, PBIGCTX_PASS ) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }
    if( !ecaffine_to_dwords( ecM, (DWORD *)f_rgbOut, &(g_pkd.ecurve), PBIGCTX_PASS ) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }
    
ErrorExit:
    PKUNLOCK;
    if( pContext )
    {
        if( pk )
        {
            BignumSecureZeroMemory( pk, (SIZEOF( digit_t) * LNGQ ) + ( SIZEOF (ecaffine_t) * 4*2*LNGQ ) );
        }

        bignum_free( pk, PBIGCTX_PASS );
    }
    return( dr );
}

#define SIGN_BUF_LEN  (LNGQDW*SIZEOF(DWORD))

static BOOL DRM_API byte_array_mod_bignum(
    const BYTE              buf[__CB_DECL(SIGN_BUF_LEN)],                                  
          digit_t           remainder[],
          PBIGCTX_ARG )               /* Given a message to be signed, in byte form reduce it modulo g_pkd.r[].  */
{

#define PACKED_CHAR_BIT (CHAR_BIT/CB_NATIVE_BYTE)
#define BYTES_PER_DIGIT (RADIX_BITS/PACKED_CHAR_BIT)

#if (RADIX_BITS % CHAR_BIT != 0) 
#error "Radix_bits not multiple of CHAR_BIT"
#endif    
    digit_t *dividend; /*[SIGN_BUF_LEN] */
    DWORD ib;

    dividend = (digit_t*)bignum_alloc( SIGN_BUF_LEN * SIZEOF( digit_t ), PBIGCTX_PASS );
    if( dividend == NULL )
    {
        return FALSE;
    }

    mp_clear(dividend, SIGN_BUF_LEN, PBIGCTX_PASS);
        
    for (ib = 0; ib < SIGN_BUF_LEN; ib++) 
    {
        DWORDC idigit = ib/BYTES_PER_DIGIT;
        DWORDC ishift = PACKED_CHAR_BIT*ib - RADIX_BITS*idigit;
        dividend[idigit] |= ((digit_t)GET_BYTE(buf,ib)) << ishift;
    }

    /* Do the division, discard quotient. */
    divide(dividend, SIGN_BUF_LEN, g_pkd.r, g_pkd.lngr, &g_pkd.rrecip, digit_NULL, remainder, PBIGCTX_PASS);
    BignumSecureZeroMemory( dividend, SIGN_BUF_LEN * SIZEOF( digit_t ) );
    bignum_free( dividend, PBIGCTX_PASS );
    return TRUE;
} /* end byte_array_mod_bignum */

static BOOL FE2IPmod(digit_tc         *fdata,
                     field_desc_tc    *fdesc,
                     digit_tc          divisor[],
                     DWORDC            ldivisor,
                     reciprocal_1_tc  *recip,
                     digit_t           remainder[],    /* OUT */
                     VOID* pContext )
                     /* Convert (i.e., cast) a field element to an integer, */
                     /* reduce the value modulo divisor.  FE2IP is a P1363 function. */
                     /* TBD -- Do we need byte reversal? */
{
    BOOL OK = TRUE;
    digit_t *mdata; /*[MP_LONGEST]; */
    digit_tc *data;
    PBIGCTX_ARG = (struct bigctx_t*) pContext;

    mdata = (digit_t*)bignum_alloc( SIZEOF( digit_t ) * MP_LONGEST, PBIGCTX_PASS );
    if( mdata == NULL )
    {
        return FALSE;
    }

    if (fdesc->ftype == FIELD_Q_MP)
    {
        from_modular(fdata, mdata, fdesc->modulo, PBIGCTX_PASS);    /* Get standard representation */
        data = mdata;
    }
    else
    {  /* Characteristic 2 */
        data = fdata;
        if( !(mp_significant_bit_count(fdata, fdesc->elng, PBIGCTX_PASS) <= fdesc->degree) )
        {
            OK = FALSE;
            /* Leading zeros expected */
        }        
    }
    if( OK )
    {
        divide(data, fdesc->elng, divisor, ldivisor, recip, digit_NULL, remainder, PBIGCTX_PASS);
    }
    BignumSecureZeroMemory( mdata, SIZEOF( digit_t ) * MP_LONGEST );
    bignum_free( mdata, PBIGCTX_PASS );
    return OK;
} /* end FE2IPmod */



/*********************************************************************
**
**  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(
    IN       DRM_VOID  *f_pContext,
    IN const PRIVKEY   *f_privkey,
    IN const DRM_BYTE  *f_pbData,
    IN       DRM_DWORD  f_cbData,
       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 );

    {
        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 );
    }

    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 */
#ifdef WMDRM_ON_SEP
#include "pkcrypto_sep.c"
#endif


/*********************************************************************
**
**  Function:  DRM_PK_Verify
**
**  Synopsis:  Verify a digital signature created by DRM_PK_Sign.
**
**  Arguments:  
**     [f_pContext]     -- Pointer to context the size of DRM_PKCRYPTO_CONTEXT_BUFFER_SIZE
**     [f_ppubkey]      -- Pubkey to check the signature with
**     [f_pbData]       -- Data buffer that the signature was created over
**     [f_cbData]       -- Length of pbBuffer in bytes
**     [f_rgbSignature] -- The signature to verify
**
**  Returns:  TRUE if the signature verified correctly.  FALSE is it didn't
**
*********************************************************************/
DRM_BOOL DRM_API DRM_PK_Verify( 
    IN       DRM_VOID  *f_pContext,
    IN const PUBKEY    *f_ppubkey, 
    IN const DRM_BYTE  *f_pbData, 
    IN       DRM_DWORD  f_cbData, 
    IN const 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 rgdwPubKey[ PK_ENC_PUBLIC_KEY_LEN / SIZEOF( DWORD ) ];
    DWORD rgdwSignature[ 2*LNGQDW ];
    DWORDREG    lgcd     = 0;
    digit_t    *sigdinv  = NULL; /* [MP_LONGEST] */
    digit_t    *gcd      = NULL; /* [MP_LONGEST] */
    digit_t    *sigc     = NULL; /* [LNGQ] */
    digit_t    *sigd     = NULL; /* [LNGQ] */
    ecaffine_t *h1G      = NULL; /* [2*MP_LONGEST] */
    ecaffine_t *h2pubkey = NULL; /* [2*MP_LONGEST] */
    ecaffine_t *pubkey   = NULL; /* [2*LNGQ] */
    BYTE       *buffer   = NULL; /* [SIGN_BUF_LEN] */
    PBIGCTX_ARG          = NULL;
    ecurve_tc  *ecurve   = NULL;
    USEPKLOCK;
    
    ChkArg( f_pContext     != NULL 
         && f_ppubkey      != 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( sigdinv = (digit_t*)bignum_alloc( (SIZEOF( digit_t) * (2*LNGQ + 2*MP_LONGEST) ) 
                                             +(SIZEOF(ecaffine_t) * (2*2*MP_LONGEST + 2*LNGQ) )
                                             + SIGN_BUF_LEN, 
                                             PBIGCTX_PASS ) );

    gcd      = sigdinv  + MP_LONGEST;
    sigc     = gcd      + MP_LONGEST;
    sigd     = sigc     + LNGQ;
    h1G      = (ecaffine_t*)(sigd     +  LNGQ);
    h2pubkey = h1G      + 2*MP_LONGEST;
    pubkey   = h2pubkey + 2*MP_LONGEST;
    buffer   = (BYTE*)(pubkey   +2*LNGQ);

    ecurve = &g_pkd.ecurve;

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

    dwords_to_digits( rgdwSignature,          sigc, LNGQDW, PBIGCTX_PASS );
    dwords_to_digits( rgdwSignature + LNGQDW, sigd, LNGQDW, PBIGCTX_PASS );

    endian_reverse_dwords( (const DWORD *)f_ppubkey->y, rgdwPubKey, PK_ENC_PUBLIC_KEY_LEN / SIZEOF( DWORD ) );

    dw_to_modular( rgdwPubKey,        LNGQDW, pubkey,      &(g_pkd.qmodulus), PBIGCTX_PASS );
    dw_to_modular( rgdwPubKey+LNGQDW, LNGQDW, pubkey+LNGQ, &(g_pkd.qmodulus), PBIGCTX_PASS );


    {
        SHA_CONTEXT shadata;

        MEMSET( buffer, 0, SIGN_BUF_LEN);

        DRM_SHA_Init( &shadata );
        DRM_SHA_Update( (BYTE*)f_pbData, f_cbData, &shadata );
        DRM_SHA_Finalize( &shadata, buffer ); 
    }


    if( !ecaffine_on_curve(pubkey, ecurve, NULL, digit_NULL, PBIGCTX_PASS) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }
    if(  all_zero(sigc, g_pkd.lngr) 
     && !all_zero(sigd, g_pkd.lngr) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }
    if( compare_same(sigc, g_pkd.r, g_pkd.lngr) >= 0 )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }
    if( compare_same(sigd, g_pkd.r, g_pkd.lngr) >= 0 )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }


    if( compare_immediate(gcd, 1, mp_gcdex(sigd, g_pkd.lngr, g_pkd.r, g_pkd.lngr, sigdinv, digit_NULL, gcd, digit_NULL, &lgcd, NULL, PBIGCTX_PASS)) != 0 )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }
    if( !byte_array_mod_bignum(buffer, gcd, PBIGCTX_PASS) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }

    multiply(sigdinv, g_pkd.lngr, gcd, g_pkd.lngr, h1G, PBIGCTX_PASS);      
    divide(h1G, 2*g_pkd.lngr, g_pkd.r, g_pkd.lngr, &(g_pkd.rrecip), digit_NULL, gcd, PBIGCTX_PASS);
    
    if( !ecaffine_exponentiation_tabular(g_pkd.TABLE, TABLE_SPACING, TABLE_LAST, gcd, g_pkd.lngr, h1G, ecurve, PBIGCTX_PASS) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }

    multiply(sigdinv, g_pkd.lngr, sigc, g_pkd.lngr, h2pubkey, PBIGCTX_PASS);
    divide(h2pubkey, 2*g_pkd.lngr, g_pkd.r, g_pkd.lngr, &(g_pkd.rrecip), digit_NULL, gcd, PBIGCTX_PASS);

    if( !ecaffine_on_curve( pubkey, ecurve, NULL, digit_NULL, PBIGCTX_PASS) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }
    if( !ecaffine_exponentiation_tabular(pubkey, TABLE_SPACING, 0, gcd, g_pkd.lngr, h2pubkey, ecurve, PBIGCTX_PASS ) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }
    
    ecaffine_addition(h1G, h2pubkey, h1G, +1, ecurve, digit_NULL, PBIGCTX_PASS );
    
    if( ecaffine_is_infinite(h1G, ecurve, PBIGCTX_PASS ) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }
    if( !FE2IPmod(h1G, ecurve->fdesc, g_pkd.r, g_pkd.lngr, &(g_pkd.rrecip), gcd, PBIGCTX_PASS ) )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }

    if( compare_same(sigc, gcd, g_pkd.lngr) != 0 )
    {
        ChkDR( DRM_E_PKCRYPTO_FAILURE );
    }

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

#ifdef __cplusplus
extern "C"
#endif
DRM_RESULT DRM_API OEM_GenRandomBytes( DRM_BYTE*, DRM_DWORD );

#ifdef __cplusplus
extern "C"
#endif
BOOL DRM_API random_bytes(BYTE* byte_array, const size_t nbyte, PBIGCTX_ARG)
{
    if( DRM_SUCCEEDED( OEM_GenRandomBytes( byte_array, nbyte) ) )
    {
        return TRUE;
    }

    return FALSE;
}


void* DRM_API bignum_alloc(const size_t cblen, PBIGCTX_ARG )
{
#if USE_REAL_MEMORY_ALLOCATION
    DRM_DWORD cchLen = 0;

    cbCur += cblen;
    if( cbCur > cbMax )
    {
        cbMax = cbCur;
    }
    return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, cblen );

#else
    VOID *pbRet = NULL;

    if( NULL == PBIGCTX_PASS )
    {  
        return OEM_malloc( cblen );
    }
    else if (DRM_FAILED (DRM_STK_Alloc ((DRM_STACK_ALLOCATOR_CONTEXT*) PBIGCTX_PASS, cblen, &pbRet)))
    {
        return NULL;
    }
    return pbRet;
#endif
}

void DRM_API bignum_free(void *pvMem, PBIGCTX_ARG)
{
    if( NULL == PBIGCTX_PASS )
    {
        SAFE_OEM_FREE( pvMem );
    }
    else if ( pvMem != NULL )
    {
#if USE_REAL_MEMORY_ALLOCATION
#if TRACK_MAX_HEAP_USAGE
        cbCur -= HeapSize( GetProcessHeap(), 0, pvMem );
#endif
        HeapFree( GetProcessHeap(), 0, pvMem );
#else

        DRM_STK_Free( (DRM_STACK_ALLOCATOR_CONTEXT*) PBIGCTX_PASS, pvMem );
#endif
    }
}

/*********************************************************************
**
**  Function:  DRM_PK_Uninitialize
**
**  Synopsis:  Uninitializes PK Crypto code
**
*********************************************************************/
DRM_VOID DRM_API DRM_PK_Uninitialize()
{
    PKUNINIT;
}

#endif // !DX_WMDRM_USE_CRYS

