/* $Id: mscrypto_rsa_encrypt.c,v 1.2 2005/01/30 18:43:31 rumen Exp $ */
#include <openssl/engine.h>
#include "mscrypto_err.h"
#include "mscrypto_int.h"


int
mscrypto_rsa_encrypt(
    int len,
    const unsigned char *from,
    unsigned char *to,
    RSA *rsa,
    int padding,
    int pubkey
) {
    int outlen = -1;

    HCRYPTKEY hKey = (HCRYPTKEY)NULL;
    DWORD dwBufLen;
    BYTE *pbData = NULL;
    DWORD dwDataLen = 0;

#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_rsa_encrypt(len=%d,..., padding=%d, pubkey=%d):\n", len, padding, pubkey);
#endif

    if (!check_padding(padding, "mscrypto_rsa_encrypt"))
        return(-1);

    hKey = mscrypto_get_hKey(rsa, pubkey);
    if (hKey == (HCRYPTKEY)NULL) {
        MSCRYPTOerr(MSCRYPTO_F_ENCRYPT, MSCRYPTO_R_KEY_IS_NOT_SET);
        goto done;
    }


    /* compute buffer size */
    dwBufLen = 0;
    if (!CryptEncrypt(
        hKey,
        (HCRYPTHASH) NULL/*hHash*/,
        (BOOL) TRUE /*Final*/,
        (DWORD) 0 /*dwFlags*/,
        pbData,
        &dwBufLen,
        dwBufLen
    )) {
        MSCRYPTOerr(MSCRYPTO_F_ENCRYPT, MSCRYPTO_R_W32ERR);
        MSCRYPTOw32err("CryptEncrypt cannot get buffer size");
        goto done;
    }

#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_rsa_encrypt() dwBufLen=%d(%d)\n",(int) dwBufLen, ((len + BN_num_bytes(rsa->n)) << 1));
#endif

    pbData = malloc(dwBufLen);
    if (pbData == NULL) {
        MSCRYPTOerr(MSCRYPTO_F_ENCRYPT, MSCRYPTO_R_INSUFFICIENT_MEMORY);
        goto done;
    }

    /* copy message into buffer */
#if 1
    memcpy(pbData, from, len);
#else
    memcpy_rev(pbData, from, len);
#endif

    /* using host key, encrypt the message */
    dwDataLen = len;
    if (!CryptEncrypt(
        hKey,
        (HCRYPTHASH) NULL /*hHash*/,
        (BOOL) TRUE /*Final*/,
        (DWORD) 0 /*dwFlags*/,
        pbData,
        &dwDataLen,
        dwBufLen
    )) {
        MSCRYPTOerr(MSCRYPTO_F_ENCRYPT, MSCRYPTO_R_W32ERR);
        MSCRYPTOw32err("CryptEncrypt fail");
        goto done;
    }

    /*
     * For some strange reason, Microsoft CryptEncrypt using
     * public key, returns the cyphertext in backwards
     * (little endian) order, so reverse it!
     */
    memcpy_rev(to, pbData, (size_t) dwDataLen);
    outlen = (int) dwDataLen;

done:
    if (pbData) free(pbData);

#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_rsa_encrypt()^ outlen=%d\n", outlen);
#endif
    return(outlen);
}
