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

#ifdef DX_WMDRM_USE_CRYS
#include <drmcommon.h>
#include <drmresults.h>
#include <drmtypes.h>
#include <oemimpl.h>
#include "CRYS.h"

/*
** The OEM_DrmRsaIdentifyKey identifies a pointer as a public key, private key, or not a key
**
** Parameters:
**
**      f_pvKey - pointer to either form of RSA key
*/
enum DRM_RSA_KEY_TYPE OEM_DrmRsaIdentifyKey(
    const DRM_VOID *f_pvKey)
{
        return DRM_RSA_KEY_TYPE_UNKNOWN;
}
/*
 * The OEM_DrmRsaSetPublicKey function allocates an internal form of an RSA public key
 *
 * Parameters:
 *
 *      f_dwPubKeyExp - Specifies the exponent of the RSA public key
 *
 *      f_pbPubKeyMod - Specifies the modulus of the RSA public key
 *
 *      f_cbPubKeyMod - Specifies the size (in bytes) of the modulus of the RSA public key
 *
 *      f_ppPublicKey - Returns a pointer to the internal form of an RSA public key
 *          Buffer must be freed using OEM_free()
 *
 * Return Value:
 *  DRM_SUCCESS - Operation completed successfully
 *  DRM_E_OUTOFMEMORY - There is no enough memory to complete the operation
 */
DRM_RESULT DRM_API
OEM_DrmRsaSetPublicKey(
    IN DRM_DWORD f_dwPubKeyExp,
    IN DRM_BYTE *f_pbPubKeyMod,
    IN DRM_DWORD f_cbPubKeyMod,
    OUT DRM_RSA_PUBLIC_KEY** f_ppPublicKey
    )
{
	return DRM_E_NOTIMPL;
}

/*
 * The OEM_DrmRsaSetPrivateKey function allocates an internal form of an RSA private key
 *
 * Parameters:
 *
 *      f_pbPrivateKey - Specifies the RSA private key.
 *          This buffer is in an OEM specific format.
 *          The format of this buffer is not necessarily the same as the one returned.
 *
 *      f_cbPrivateKey - Specifies the size (in bytes) of the RSA private key
 *
 *      f_ppPrivateKey - Returns the RSA private key.
 *          Buffer must be freed using OEM_free()
 *
 * Return Value:
 *  DRM_SUCCESS - Operation completed successfully
 *  DRM_E_OUTOFMEMORY - There is no enough memory to complete the operation
 */

DRM_RESULT DRM_API
OEM_DrmRsaSetPrivateKey(
    IN DRM_BYTE *f_pbPrivateKey,
    IN DRM_DWORD f_cbPrivateKey,
    OUT DRM_RSA_PRIVATE_KEY **f_ppPrivateKey
    )
{
	return DRM_E_NOTIMPL;
}

/*
 * The OEM_DrmRsaKeySize routine determines the key size for the specific key.
 *
 * The key size is the size of the buffer used during encryption or decryption with the key.
 * A buffer of this size should be passed into the DrmRsaEncPublic and DrmRsaDecPrivate routines.
 *
 * Notice that this key size shouldn't be used for any other purpose.  Specifically,
 * note that this size may be larger then DrmRsaModulusSize.
 *
 * Parameters:
 *
 *      f_pvKey - Specifies the key to determine the size of
 *          This key must be either of type DRM_RSA_PUBLIC_KEY or DRM_RSA_PRIVATE_KEY.
 *          NULL indicates that the private key baked into the device should be used
 *
 * Return Value:
 *      Size of the key in bytes
 *      0 indicates that the key isn't properly formatted
 */

DRM_DWORD DRM_API
OEM_DrmRsaKeySize(
    const DRM_VOID *f_pvKey
    )
{
	return DRM_E_NOTIMPL;
}

/*
 * The OEM_DrmRsaModulusSize routine determines the modulus size for the specific key.
 *
 * The modulus size represents the publicly visible strength of the key.
 *
 * Parameters:
 *
 *      f_pvKey - Specifies the key to determine the modulus size of
 *          This key must be either of type DRM_RSA_PUBLIC_KEY or DRM_RSA_PRIVATE_KEY.
 *          NULL indicates that the private key baked into the device should be used
 *
 * Return Value:
 *      Modulus size of the key in bytes
 *      0 indicates that the key isn't properly formatted
 */

DRM_DWORD DRM_API
OEM_DrmRsaModulusSize(
    DRM_VOID *f_pvKey
    )
{
	return DRM_E_NOTIMPL;
}


/*
 * The OEM_DrmRsaGetBufferSize routine determines the size for the specific key.
 *
 * Parameters:
 *
 *      f_pvKey - Specifies the key to determine the size of
 *          This key must be either of type DRM_RSA_PUBLIC_KEY or DRM_RSA_PRIVATE_KEY.
 *
 *      f_pcbKey- on success this is filled in with the buffer size for the key
 *
 * Notes: while public keys are intrinsically self-describing, RSA private keys
 *        only reveal their total size by the implicit relationship between the 
 *        size of the public key modulus and the buffer size
 */
DRM_RESULT DRM_API
OEM_DrmRsaGetBufferSize (
    IN  const DRM_VOID  *f_pvKey,
    OUT       DRM_DWORD *f_pcbKey )
{
	return DRM_E_NOTIMPL;
}


/*
 * The OEM_DrmRsaEncPublic routine RSA encrypts a fixed size buffer with an RSA public key
 *
 * Parameters:
 *
 *      f_pKey - Specifies the public key
 *
 *      f_pbDataIn - Specifies the input data
 *          The array must be DrmRsaKeySize bytes long.  Any unused bytes in the
 *          array should be zeroed.
 *
 *      f_pbDataOut - Specifies the output data
 *          The array must be DrmRsaKeySize() bytes long.
 *
 * Return Value:
 *      TRUE - encryption succeeded.
 *      FALSE - encryption failed.
 */

DRM_BOOL DRM_API
OEM_DrmRsaEncPublic(
    IN DRM_RSA_PUBLIC_KEY *f_pKey,
    IN DRM_BYTE *f_pbDataIn,
    IN OUT DRM_BYTE *f_pbDataOut
    )
{
	return DRM_E_NOTIMPL;
}


/*
 * The OEM_DrmRsaDecPrivate routine RSA decrypts a fixed size buffer with an RSA private key
 *
 * Parameters:
 *
 *      f_pKey - Specifies the private key
 *          NULL indicates that the private key baked into the device should be used
 *
 *      f_pbDataIn - Specifies the input data
 *          The array must be DrmRsaKeySize bytes long.  Any unused bytes in the
 *          array should be zeroed.
 *
 *      f_pbDataOut - Specifies the output data
 *          The array must be DrmRsaKeySize() bytes long.
 *
 * Return Value:
 *      TRUE - decryption succeeded.
 *      FALSE - decryption failed.
 */

DRM_BOOL DRM_API
OEM_DrmRsaDecPrivate(
    IN DRM_RSA_PRIVATE_KEY *f_pKey,
    IN DRM_BYTE *f_pbDataIn,
    IN OUT DRM_BYTE *f_pbDataOut
    )
{
	return DRM_E_NOTIMPL;
}

/* parsing the public key */
DRM_RESULT DRM_API OEM_DrmRsaParsePublicKey(
    IN    DRM_RSA_PUBLIC_KEY *f_pkeyRSAPublic,
    OUT         DRM_DWORD    *f_pdwExponent,
    OUT         DRM_BYTE     **f_ppbModulus,
    OUT         DRM_DWORD    *f_pcbModulus)
{
	return DRM_E_NOTIMPL;
}

/* parsing the private key */
DRM_RESULT DRM_API OEM_DrmRsaParsePrivateKey(
    IN    DRM_RSA_PRIVATE_KEY *f_pkeyRSAPrivate,
    OUT   DRM_DWORD           *f_pdwExponent,
    OUT   DRM_BYTE           **f_ppbModulus,
    OUT   DRM_DWORD           *f_pcbModulus,
    OUT   DRM_BYTE           **f_ppbPrivate,
    OUT   DRM_DWORD           *f_pcbPrivate)
{
	return DRM_E_NOTIMPL;
}


/*
 * The OEM_DrmRsaReleaseKey routines free keys created with OEM_DrmRsaSetPublicKey and OEM_DrmRsaSetPrivateKey
 *
 * Parameters:
 *
 *      f_pvKey - Specifies the key
 *
 * Return Value:
 *      DRM_E_INVALIDARG - the given pointer does not point to a valid RSA key struct
 */
DRM_RESULT DRM_API OEM_DrmRsaReleaseKey(
       IN DRM_VOID *f_pvKey)
{
	return DRM_E_NOTIMPL;
}


#else
#include <drmcommon.h>
#include <drmresults.h>
#include <drmtypes.h>
#include <oemimpl.h>
#include <bignum.h>
#include "rsaimpl.h"


#define PUB_KEY_MARK 0
#define PRIV_KEY_MARK 1

/*
** The OEM_DrmRsaIdentifyKey identifies a pointer as a public key, private key, or not a key
**
** Parameters:
**
**      f_pvKey - pointer to either form of RSA key
*/
enum DRM_RSA_KEY_TYPE OEM_DrmRsaIdentifyKey(
    const DRM_VOID *f_pvKey)
{
    DRM_BYTE *helper = (DRM_BYTE*)f_pvKey;
    if (f_pvKey == 0) 
        return DRM_RSA_KEY_TYPE_UNKNOWN;
    if (GET_BYTE(helper, 0) == PRIV_KEY_MARK)
        return DRM_RSA_KEY_TYPE_PRIVATE;
    else if (GET_BYTE(helper,0) == PUB_KEY_MARK)
        return DRM_RSA_KEY_TYPE_PUBLIC;
    else
        return DRM_RSA_KEY_TYPE_UNKNOWN;
}

/*
 * The OEM_DrmRsaSetPublicKey function allocates an internal form of an RSA public key
 *
 * Parameters:
 *
 *      f_dwPubKeyExp - Specifies the exponent of the RSA public key
 *
 *      f_pbPubKeyMod - Specifies the modulus of the RSA public key
 *
 *      f_cbPubKeyMod - Specifies the size (in bytes) of the modulus of the RSA public key
 *
 *      f_ppPublicKey - Returns a pointer to the internal form of an RSA public key
 *          Buffer must be freed using OEM_free()
 *
 * Return Value:
 *  DRM_SUCCESS - Operation completed successfully
 *  DRM_E_OUTOFMEMORY - There is no enough memory to complete the operation
 */
DRM_RESULT DRM_API
OEM_DrmRsaSetPublicKey(
    IN DRM_DWORD f_dwPubKeyExp,
    IN DRM_BYTE *f_pbPubKeyMod,
    IN DRM_DWORD f_cbPubKeyMod,
    OUT DRM_RSA_PUBLIC_KEY** f_ppPublicKey
    )
{
    DRM_RESULT dr = DRM_SUCCESS;

    /* length of public exponent in bits */
    DRM_DWORD bitlen_pubexp = (DRM_DWORD)significant_bit_count( f_dwPubKeyExp );
    /* length of public expoenent in bytes */
    DRM_DWORD bytlen_pubexp = (bitlen_pubexp+7)/8;
    /* length of modulus in bytes */
    DRM_DWORD bytlen_mod = f_cbPubKeyMod;
    /* pointer to an external rsa key */
    DRM_BYTE *blobkey = 0;
    external_rsa_key_t *newkey = NULL;
#ifdef REVERSE_MODULUS
    DRM_DWORD i;
#endif

    ChkArg( f_pbPubKeyMod != NULL
         && f_ppPublicKey != NULL );

    blobkey = (DRM_BYTE*)OEM_malloc( SIZEOF(DRM_DWORD) + SIZEOF(external_rsa_key_t) );
    ChkMem( blobkey );

    PUT_BYTE(blobkey, 0, PUB_KEY_MARK);
    newkey = (external_rsa_key_t*)(&blobkey[sizeof(DRM_DWORD)]);
    /* figure out the length of the pubexp */
    newkey->bitlen_pubexp = bitlen_pubexp;
    /* figure out the length of the modulus (must be a multiple of 8) */
    newkey->bitlen_modulus = f_cbPubKeyMod * 8;
    /* allocate memory for the key */
#if __TMS320C55X__
    newkey->free_me = (DRM_BYTE*)OEM_malloc( (1 * bytlen_mod + bytlen_pubexp) * SIZEOF(DRM_BYTE) );
#else
    newkey->free_me = (DRM_BYTE*)OEM_malloc( (3 * bytlen_mod + bytlen_pubexp) * SIZEOF(DRM_BYTE) );
#endif
    /*
       DARKO: NOTE THAT 3*bytlen_mod ARE ALLOCATED. TWO OF THEM ARE NOT USED IN
       THE CURRENT CALLS OF DRM (USED TO STORE THE INDIVIDUAL PRIMES AND THEIR INVERSES)...
       IF SPACE IN OPERATING MEMORY IS PROBLEM, CONSIDER SHAVING OFF THIS DATA
       FROM THE EXTERNAL KEY!!!
    */
    ChkMem( newkey->free_me );
    /* arrange the pointers */
    newkey->pubexp = newkey->free_me;
    newkey->modulus = newkey->pubexp + __CB_DECL(bytlen_pubexp);
    /* store the public exponent */
    digits_to_big_endian_bytes( (digit_tc*)&f_dwPubKeyExp, bitlen_pubexp, newkey->pubexp );

#ifdef REVERSE_MODULUS
    /* Reverse the modulus */
    for (i = 0; i < f_cbPubKeyMod; ++i)
    {
        PUT_BYTE( (DRM_BYTE *)newkey->modulus,
            i,
            GET_BYTE( f_pbPubKeyMod, f_cbPubKeyMod - i - 1 ) );
    }
#else
    /* copy modulus  */
    MEMCPY( newkey->modulus, f_pbPubKeyMod, bytlen_mod );
#endif

    /* finally recast for the output */
    *f_ppPublicKey = (DRM_RSA_PUBLIC_KEY*)blobkey;

ErrorExit:

    if (DRM_FAILED(dr))
    {
        if (newkey != NULL)
        {
            if (newkey->free_me != NULL)
            {
                OEM_free( newkey->free_me );
            }
            OEM_free( newkey );
        }
    }

    return dr;

}

/*
 * The OEM_DrmRsaSetPrivateKey function allocates an internal form of an RSA private key
 *
 * Parameters:
 *
 *      f_pbPrivateKey - Specifies the RSA private key.
 *          This buffer is in an OEM specific format.
 *          The format of this buffer is not necessarily the same as the one returned.
 *
 *      f_cbPrivateKey - Specifies the size (in bytes) of the RSA private key
 *
 *      f_ppPrivateKey - Returns the RSA private key.
 *          Buffer must be freed using OEM_free()
 *
 * Return Value:
 *  DRM_SUCCESS - Operation completed successfully
 *  DRM_E_OUTOFMEMORY - There is no enough memory to complete the operation
 */

DRM_RESULT DRM_API
OEM_DrmRsaSetPrivateKey(
    IN DRM_BYTE *f_pbPrivateKey,
    IN DRM_DWORD f_cbPrivateKey,
    OUT DRM_RSA_PRIVATE_KEY **f_ppPrivateKey
    )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_BOOL   OK = TRUE;
    DRM_BYTE  *help;
    DRM_BYTE  *blobkey = 0;
    DRM_DWORD  diglen_mod, primelen;
    digit_t   *dtemps = NULL;
    digit_t   *temp1 = NULL;
    digit_t   *temp2 = NULL;
    digit_t   *temp3 = NULL;
    internal_rsa_key_t *key = NULL;
    DRM_DWORD *prime = NULL;
    DRM_DWORD *primesrc;
    DRM_DWORD i;

    ChkArg( f_pbPrivateKey != NULL
         && f_ppPrivateKey != NULL );


    /* make sure that the buffer is at least the minimum size */
    ChkArg( f_cbPrivateKey >= 4 /* exponent */
                        + 4 /* bitlen modulus */
                        + 4 /* length of the primes */ );
    /* allocate the new private key */
    blobkey = (DRM_BYTE*)OEM_malloc( SIZEOF(DRM_DWORD) + SIZEOF(internal_rsa_key_t) );
    ChkMem( blobkey );
    PUT_BYTE(blobkey, 0, PRIV_KEY_MARK);
    key = (internal_rsa_key_t*)(&blobkey[sizeof(DRM_DWORD)]);
    /* how does the external structure look like? */
    /* diglen_pubexp has always length of one DWORD - DRM API says so! */
    key->diglen_pubexp = 1; 
    /* bitlen_modulus is stored in the 4-7th bytes of *f_pbPrivateKey */
    BYTES_TO_DWORD(key->bitlen_modulus, f_pbPrivateKey + __CB_DECL(4));
    /* length of primes is stored in the 8-11th bytes of *f_pbPrivateKey */
    /* both primes must be of equal length, i.e. 1/2 of modulus bitlen */
    BYTES_TO_DWORD(primelen, f_pbPrivateKey + __CB_DECL(8));
    /* make sure that the given buffer is the right size */
    ChkArg( f_cbPrivateKey >= 4 /* exponent */
                        + 4 /* bitlen modulus */
                        + 4 /* length of the primes */
                        + 2 * primelen * 4 /* primes */ );
    /* length of modulus */
    diglen_mod = 2 * primelen;
    /* allocate memory for private key structure... */
    dtemps = digit_allocate( 3 * diglen_mod + key->diglen_pubexp, "rsa_key_internalize", NULL);
    ChkMem( dtemps );
    /* set pointers appropriately */
    key->free_me = dtemps;
    key->pubexp = dtemps + 3 * diglen_mod;
    /* value of public exponent is stored in the 0-3rd bytes of *f_pbPrivateKey */
    /* the value can be only a single DWORD since key->diglen_pubexp must be one */
    BYTES_TO_DWORD(key->pubexp[0], &f_pbPrivateKey[0]);
    /* compute the modulus from the primes */
    key->modulus = dtemps;
    /* readjust pointer to second prime  */
    ChkMem( prime = OEM_malloc( primelen * SIZEOF(DRM_DWORD) * 2) );
    for(i = 0, primesrc = (DRM_DWORD *)&f_pbPrivateKey[__CB_DECL(12)]; i < primelen * 2; i++, primesrc++)
    {
        BYTES_TO_DWORD(prime[i], primesrc);
    }
    help = (DRM_BYTE *)&prime[primelen];

    /* compute the modulus */
    OK = OK && multiply((digit_tc *)prime, primelen,
        (digit_tc *)help, primelen, key->modulus, NULL);
    if(!OK)
    {
        TRACE(("multiply failed\n"));
    }

    if (OK)
    {
        /* temporary buffers to store intermediate results... */
        DWORDREG ip, moduli_created = 0;
        DWORDREG lgcd = 0;

        temp1 = (digit_t*)OEM_malloc(SIZEOF(digit_t)*primelen);    
        ChkMem( temp1 );
        temp2 = (digit_t*)OEM_malloc(SIZEOF(digit_t)*primelen);    
        ChkMem( temp2 );
        temp3 = (digit_t*)OEM_malloc(SIZEOF(digit_t)*primelen);    
        ChkMem( temp3 );

        /* for each prime */
        for (ip = 0; ip != 2; ip++) {
            /* pointer to the prime */
            digit_t *src;
			
            for(i = 0, src = (digit_t *)(&f_pbPrivateKey[__CB_DECL(12)]) + ip*primelen; i < primelen; i++, src++)
            {
                BYTES_TO_DWORD(temp1[i], src);
            }
            /* pointer to the private exponents */
            key->privexps[ip] = dtemps + diglen_mod + ip*primelen;
            /* pointer to chinese */
            key->chineses[ip] = key->privexps[ip] + diglen_mod;
            /* compute the modulus strucutre for each prime */
            OK = OK && create_modulus(temp1, primelen, FROM_RIGHT, &key->moduli[ip], NULL, NULL);

            if (OK)
                moduli_created++;
            /* compute the private exponent */
            sub_immediate(temp1, 1, temp2, primelen);    /* temp2 = p - 1 */
            OK = OK && mp_gcdex(key->pubexp, key->diglen_pubexp, temp2, primelen,
                        key->privexps[ip], digit_NULL, temp3,
                        digit_NULL, &lgcd, digit_NULL, NULL);

        } /* for ip */
        /* compute the chinese */
        OK = OK && mp_gcdex(key->moduli[0].modulus, primelen,
            key->moduli[1].modulus, primelen,
            key->chineses[1], key->chineses[0],
            temp2, digit_NULL, &lgcd, digit_NULL, NULL);

        /* chineses[0] = 1/p2 (mod p1).  chineses[1] = 1/p1 (mod p2) */
        if (OK && compare_immediate(temp2, 1, lgcd) != 0) {
            if(!OK)
            {
                TRACE(("compare_immediate failed\n"));
            }
            OK = FALSE;
            SetMpErrno_clue(MP_ERRNO_INVALID_DATA,
                "rsa_key_internalize, GCD(p1, p2) <> 1", NULL);
        }
        OEM_free(temp1);
        OEM_free(temp2);
        OEM_free(temp3);
    } /* if */
    /* recast the output */
    *f_ppPrivateKey = (DRM_RSA_PRIVATE_KEY*)blobkey;

ErrorExit:

    if(!OK)
    {
        dr = DRM_E_FAIL;
    }
    
    if (DRM_FAILED(dr))
    {
        if (blobkey != NULL)
        {
            OEM_free( blobkey );
        }
        if (dtemps != NULL)
        {
            OEM_free( dtemps );
        }
        if (temp2 != NULL)
        {
            OEM_free( temp2 );
        }
        if (temp3 != NULL)
        {
            OEM_free( temp3 );
        }
    }
    SAFE_OEM_FREE( prime );

    return dr;

}

/*
 * The OEM_DrmRsaKeySize routine determines the key size for the specific key.
 *
 * The key size is the size of the buffer used during encryption or decryption with the key.
 * A buffer of this size should be passed into the DrmRsaEncPublic and DrmRsaDecPrivate routines.
 *
 * Notice that this key size shouldn't be used for any other purpose.  Specifically,
 * note that this size may be larger then DrmRsaModulusSize.
 *
 * Parameters:
 *
 *      f_pvKey - Specifies the key to determine the size of
 *          This key must be either of type DRM_RSA_PUBLIC_KEY or DRM_RSA_PRIVATE_KEY.
 *          NULL indicates that the private key baked into the device should be used
 *
 * Return Value:
 *      Size of the key in bytes
 *      0 indicates that the key isn't properly formatted
 */

DRM_DWORD DRM_API
OEM_DrmRsaKeySize(
    const DRM_VOID *f_pvKey
    )
{
    DRM_BYTE *helper = (DRM_BYTE*)f_pvKey;
    if (f_pvKey == 0) 
        return 0;
    if (GET_BYTE(helper, 0) == PRIV_KEY_MARK)
    {
      internal_rsa_key_t *key = (internal_rsa_key_t*)(&helper[sizeof(DRM_DWORD)]);
        return (key->bitlen_modulus+7)/8;
    }
    else if (GET_BYTE(helper, 0) == PUB_KEY_MARK)
    {
      external_rsa_key_t *key = (external_rsa_key_t*)(&helper[sizeof(DRM_DWORD)]);
        return (key->bitlen_modulus+7)/8;
    }
    else
        return 0;
}

/*
 * The OEM_DrmRsaModulusSize routine determines the modulus size for the specific key.
 *
 * The modulus size represents the publicly visible strength of the key.
 *
 * Parameters:
 *
 *      f_pvKey - Specifies the key to determine the modulus size of
 *          This key must be either of type DRM_RSA_PUBLIC_KEY or DRM_RSA_PRIVATE_KEY.
 *          NULL indicates that the private key baked into the device should be used
 *
 * Return Value:
 *      Modulus size of the key in bytes
 *      0 indicates that the key isn't properly formatted
 */

DRM_DWORD DRM_API
OEM_DrmRsaModulusSize(
    DRM_VOID *f_pvKey
    )
{
    DRM_BYTE *helper = (DRM_BYTE*)f_pvKey;
    if (f_pvKey == 0) 
        return 0;
    if (GET_BYTE(helper, 0) == PRIV_KEY_MARK)
    {
        internal_rsa_key_t *key = (internal_rsa_key_t*)(&helper[sizeof(DRM_DWORD)]);
        return (key->bitlen_modulus+7)/8;
    }
    else if (GET_BYTE(helper, 0) == PUB_KEY_MARK)
    {
      external_rsa_key_t *key = (external_rsa_key_t*)(&helper[sizeof(DRM_DWORD)]);
        return (key->bitlen_modulus+7)/8;
    }
    else
    {
		TRACE(("Invalid key type %d\n", GET_BYTE(helper, 0)));
        return 0;
	}
}


/*
 * The OEM_DrmRsaGetBufferSize routine determines the size for the specific key.
 *
 * Parameters:
 *
 *      f_pvKey - Specifies the key to determine the size of
 *          This key must be either of type DRM_RSA_PUBLIC_KEY or DRM_RSA_PRIVATE_KEY.
 *
 *      f_pcbKey- on success this is filled in with the buffer size for the key
 *
 * Notes: while public keys are intrinsically self-describing, RSA private keys
 *        only reveal their total size by the implicit relationship between the 
 *        size of the public key modulus and the buffer size
 */
DRM_RESULT DRM_API
OEM_DrmRsaGetBufferSize (
    IN  const DRM_VOID  *f_pvKey,
    OUT       DRM_DWORD *f_pcbKey )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_BYTE *helper;

    ChkArg( f_pvKey  != NULL
         && f_pcbKey != NULL );

    helper = (DRM_BYTE*)f_pvKey;
    if (GET_BYTE(helper, 0) == PRIV_KEY_MARK)
    {
        internal_rsa_key_t *key = (internal_rsa_key_t*)(&helper[sizeof(DRM_DWORD)]);
        *f_pcbKey = (key->bitlen_modulus+7)/8;
    }
    else if (GET_BYTE(helper, 0) == PUB_KEY_MARK)
    {
        external_rsa_key_t *key = (external_rsa_key_t*)(&helper[sizeof(DRM_DWORD)]);
        *f_pcbKey = (key->bitlen_modulus+7)/8;
    }
    else
        ChkDR( DRM_E_FAIL );

ErrorExit:

    return dr;
}

/*
 * The OEM_DrmRsaEncPublic routine RSA encrypts a fixed size buffer with an RSA public key
 *
 * Parameters:
 *
 *      f_pKey - Specifies the public key
 *
 *      f_pbDataIn - Specifies the input data
 *          The array must be DrmRsaKeySize bytes long.  Any unused bytes in the
 *          array should be zeroed.
 *
 *      f_pbDataOut - Specifies the output data
 *          The array must be DrmRsaKeySize() bytes long.
 *
 * Return Value:
 *      TRUE - encryption succeeded.
 *      FALSE - encryption failed.
 */

DRM_BOOL DRM_API
OEM_DrmRsaEncPublic(
    IN DRM_RSA_PUBLIC_KEY *f_pKey,
    IN DRM_BYTE *f_pbDataIn,
    IN OUT DRM_BYTE *f_pbDataOut
    )
{
    if (f_pKey != 0 && f_pbDataIn != 0 && f_pbDataOut != 0)
    {
        DRM_BYTE *helper = (DRM_BYTE*)f_pKey;
        external_rsa_key_t *mf = (external_rsa_key_t*)(&helper[sizeof(DRM_DWORD)]);
        
        return ((DRM_BOOL) rsa_encryption(mf, 
            (DRM_BYTE*)f_pbDataIn, (DRM_BYTE*)f_pbDataOut)); 
    }
    else return FALSE;
}

/*
 * The OEM_DrmRsaDecPrivate routine RSA decrypts a fixed size buffer with an RSA private key
 *
 * Parameters:
 *
 *      f_pKey - Specifies the private key
 *          NULL indicates that the private key baked into the device should be used
 *
 *      f_pbDataIn - Specifies the input data
 *          The array must be DrmRsaKeySize bytes long.  Any unused bytes in the
 *          array should be zeroed.
 *
 *      f_pbDataOut - Specifies the output data
 *          The array must be DrmRsaKeySize() bytes long.
 *
 * Return Value:
 *      TRUE - decryption succeeded.
 *      FALSE - decryption failed.
 */

DRM_BOOL DRM_API
OEM_DrmRsaDecPrivate(
    IN DRM_RSA_PRIVATE_KEY *f_pKey,
    IN DRM_BYTE *f_pbDataIn,
    IN OUT DRM_BYTE *f_pbDataOut
    )
{
    DRM_BYTE *helper;
    internal_rsa_key_t *mf;

    if ( f_pKey      == NULL
      || f_pbDataIn  == NULL
      || f_pbDataOut == NULL )
    {
        return FALSE;
    }

    helper = (DRM_BYTE*)f_pKey;
    mf = (internal_rsa_key_t*)(&helper[sizeof(DRM_DWORD)]);
    return rsa_decryption( mf, (DRM_BYTE*)f_pbDataIn, (DRM_BYTE*)f_pbDataOut );
}

/* parsing the public key */
DRM_RESULT DRM_API OEM_DrmRsaParsePublicKey(
    IN    DRM_RSA_PUBLIC_KEY *f_pkeyRSAPublic,
    OUT         DRM_DWORD    *f_pdwExponent,
    OUT         DRM_BYTE     **f_ppbModulus,
    OUT         DRM_DWORD    *f_pcbModulus)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_BYTE *helper;
    external_rsa_key_t *key;
    DRM_DWORD cbModulus;

    ChkArg( f_pkeyRSAPublic != NULL );

    /* cast the key into bignum's internal structure */
    helper = (DRM_BYTE*)f_pkeyRSAPublic;
    key = (external_rsa_key_t*)(&helper[sizeof(DRM_DWORD)]);    

    /* figure out how large the contents are in bytes */
    cbModulus = (key->bitlen_modulus + 7)/8;

    if ( f_pcbModulus != NULL )
    {
        *f_pcbModulus = cbModulus;
    }

    if ( f_pdwExponent != NULL )
    {
        /* copy contents of exponent */
        big_endian_bytes_to_digits(key->pubexp, key->bitlen_pubexp, (digit_t*)f_pdwExponent);
    }

    if (f_ppbModulus != NULL)
    {
        *f_ppbModulus = key->modulus;
    }

ErrorExit:

    return dr;
}

/* parsing the private key */
DRM_RESULT DRM_API OEM_DrmRsaParsePrivateKey(
    IN    DRM_RSA_PRIVATE_KEY *f_pkeyRSAPrivate,
    OUT   DRM_DWORD           *f_pdwExponent,
    OUT   DRM_BYTE           **f_ppbModulus,
    OUT   DRM_DWORD           *f_pcbModulus,
    OUT   DRM_BYTE           **f_ppbPrivate,
    OUT   DRM_DWORD           *f_pcbPrivate)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_DWORD bytlen_modulus;
    DRM_BYTE *help;
    DRM_BYTE *helper;
    internal_rsa_key_t *key;

    ChkArg( f_pdwExponent != NULL
         && f_ppbModulus  != NULL
         && f_pcbModulus  != NULL
         && f_ppbPrivate  != NULL
         && f_pcbPrivate  != NULL );

    helper = (DRM_BYTE*)f_pkeyRSAPrivate;
    key = (internal_rsa_key_t*)(&helper[sizeof(DRM_DWORD)]);    
    /* fix pointer to key */
    /* figure out length of pub-key exponent */
    *f_pdwExponent = key->pubexp[0];
    /* length of modulus in bits */
    *f_pcbModulus = key->bitlen_modulus;
    bytlen_modulus = ((*f_pcbModulus)+7)/8;
    /* copy the modulus */
    *f_ppbModulus = (DRM_BYTE*)OEM_malloc(SIZEOF(DRM_BYTE) * bytlen_modulus);
    ChkMem( f_ppbModulus );
    MEMCPY(*f_ppbModulus, key->modulus, bytlen_modulus);
    /* copy the prime lengths */
    if (key->moduli[0].length != key->moduli[1].length)
    {
        OEM_free(*f_ppbModulus);
        *f_ppbModulus = 0;
        ChkDR( DRM_E_FAIL );
    }
    *f_pcbPrivate = (DRM_DWORD)key->moduli[0].length;
    /* allocate memory for the primes */
    *f_ppbPrivate = (DRM_BYTE*)OEM_malloc(SIZEOF(DRM_BYTE) * bytlen_modulus);
    if (*f_ppbPrivate == NULL)
    {
        OEM_free(*f_ppbModulus);
        *f_ppbModulus = 0;
        ChkDR( DRM_E_FAIL );
    }
    /* copy the first prime */
    MEMCPY(*f_ppbPrivate, key->moduli[0].modulus, (*f_pcbPrivate)*4);
    /* then the second prime */
    help = (*f_ppbPrivate)+(*f_pcbPrivate)*4;
    MEMCPY(help, key->moduli[1].modulus, (*f_pcbPrivate)*4);

ErrorExit:

    return dr;
}


/*
 * The OEM_DrmRsaReleaseKey routines free keys created with OEM_DrmRsaSetPublicKey and OEM_DrmRsaSetPrivateKey
 *
 * Parameters:
 *
 *      f_pvKey - Specifies the key
 *
 * Return Value:
 *      DRM_E_INVALIDARG - the given pointer does not point to a valid RSA key struct
 */
DRM_RESULT DRM_API OEM_DrmRsaReleaseKey(
       IN DRM_VOID *f_pvKey)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_BYTE *helper;

    if ( f_pvKey != NULL )
    {
        helper = (DRM_BYTE*) f_pvKey;

        if (GET_BYTE(helper, 0) == PUB_KEY_MARK)
        {
            external_rsa_key_t *prsaext = (external_rsa_key_t*)(&helper[sizeof(DRM_DWORD)]);
            Free_Temporaries(prsaext->free_me, NULL);
            OEM_SecureZeroMemory( (DRM_BYTE *) prsaext, SIZEOF(external_rsa_key_t) );
            OEM_free( f_pvKey );
        }
        else if (GET_BYTE(helper, 0) == PRIV_KEY_MARK)
        {
            internal_rsa_key_t *prsaint = (internal_rsa_key_t*)(&helper[sizeof(DRM_DWORD)]);
            uncreate_modulus(&prsaint->moduli[0], NULL);
            uncreate_modulus(&prsaint->moduli[1], NULL);
            Free_Temporaries(prsaint->free_me, NULL);
            OEM_SecureZeroMemory( (DRM_BYTE *) prsaint, SIZEOF(internal_rsa_key_t) );
            OEM_free( f_pvKey );
        }
        else
            ChkDR( DRM_E_FAIL );
    }

ErrorExit:

    return dr;
}


#endif //#ifndef DX_WMDRM_USE_CRYS

