/* $Id: mscrypto.c,v 1.2 2005/01/30 20:07:38 rumen Exp $ */
#include <stdio.h>
#include <string.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/bn.h>
#include <openssl/engine.h>
#ifndef ENGINE_CMD_BASE
#  include "ENGINE_CMD_BASE is not defined"
#endif

#define MSCRYPTO_LIB_NAME "mscrypto engine"
#include "mscrypto_err.c"
#include "mscrypto_int.h"


#if defined(__MINGW32__)
#include <wincrypt.h>

#define CERT_SYSTEM_STORE_CURRENT_USER		0x00010000UL
/*#define CERT_SYSTEM_STORE_LOCAL_MACHINE		0x00020000UL*/

/* Certificate Store close flags */
#define CERT_CLOSE_STORE_FORCE_FLAG		0x00000001
#define CERT_CLOSE_STORE_CHECK_FLAG		0x00000002

#endif


BIO *logstream = NULL;
int/*bool*/ quiet_flag = 0;
static wchar_t *store_name = NULL;


/*
 * Constants used when creating the ENGINE
 */
static const char *mscrypto_engine_id = "mscrypto";
static const char *mscrypto_engine_name = "MSCrypto engine support";

/*
 * Functions to handle the engine
 */
static int mscrypto_destroy(ENGINE *e);
static int mscrypto_init(ENGINE *e);
static int mscrypto_finish(ENGINE *e);

static int/*bool*/ mscrypto_open_store(MSCRYPTO_CTX *ctx);
static void        mscrypto_close_store(MSCRYPTO_CTX *ctx);

/*
 * Engine commands
 */
#define CMD_SO_PATH        (ENGINE_CMD_BASE)
#define CMD_MODULE_PATH    (ENGINE_CMD_BASE+1)
#define CMD_STORE_NAME     (ENGINE_CMD_BASE+2)
#define CMD_PIN            (ENGINE_CMD_BASE+3)
#define CMD_QUIET          (ENGINE_CMD_BASE+4)

static const ENGINE_CMD_DEFN mscrypto_cmd_defns[] = {
    {CMD_SO_PATH,
     "SO_PATH",
     "Specifies the path to the 'mscrypto-engine' shared library",
     ENGINE_CMD_FLAG_STRING},
    {CMD_MODULE_PATH,
     "MODULE_PATH",
     "Specifies the path to the mscrypto module shared library",
     ENGINE_CMD_FLAG_STRING},
    {CMD_STORE_NAME,
     "STORE_NAME",
     "Specifies the certificate store name",
     ENGINE_CMD_FLAG_STRING},
    {CMD_PIN,
     "PIN",
     "Specifies the pin code",
     ENGINE_CMD_FLAG_STRING},
    {CMD_QUIET,
     "QUIET",
     "Switch additional details printing",
     ENGINE_CMD_FLAG_NO_INPUT},
    {0, NULL, NULL, 0}
};


#if 0
int
mscrypto_log(const char *format, ...) {
        va_list args;
        int ret;

#ifdef MSCRYPTO_TRACE
        va_start(args, format);
        ret = vfprintf(stderr, format, args);
        va_end(args);
#else
        if (logstream == NULL)	return(0);

        va_start(args, format);
        ret = BIO_vprintf(logstream, format, args);
        va_end(args);
#endif

        return(ret);
}
#endif


static int/*bool*/
mscrypto_cmd_store_name(const char *s) {
    int ret = 0;

    wchar_t *p;
    size_t name_len;
    int n;

    if (s == NULL) {
        MSCRYPTOerr(MSCRYPTO_F_CMD_STORE_NAME, MSCRYPTO_R_INVALID_ARGUMENT);
        goto done;
    }

    if (store_name) {
        free(store_name);
        store_name = NULL;
    }

    n = strlen(s);
    p = malloc((n + 1) * sizeof(wchar_t));
    if (p == NULL) {
        MSCRYPTOerr(MSCRYPTO_F_CMD_STORE_NAME, MSCRYPTO_R_INSUFFICIENT_MEMORY);
        goto done;
    }

    name_len = mbstowcs(p, s, n);
#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_cmd_store_name() store_name=%s, n=%d, name_len=%d\n", s, (int) n, (int) name_len);
#endif

    if (name_len == (size_t) -1) {
        MSCRYPTOerr(MSCRYPTO_F_CMD_STORE_NAME, MSCRYPTO_R_MBSTOWCS_FAIL);
        free(p);
        goto done;
    }
    p[name_len] = 0;
    store_name = p;
    ret = 1;

done:
    return(ret);
}


static int/*bool*/
mscrypto_ctrl(
    ENGINE *e,
    int cmd,
    long i,
    void *p,
    void (*f)()
) {
    int ret = 0;

    UNUSED(e);
    UNUSED(i);
    UNUSED(f);

    switch (cmd) {
    case ENGINE_CTRL_SET_LOGSTREAM: {
        BIO *bio = (BIO *)p;

#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_ctrl() ENGINE_CTRL_SET_LOGSTREAM\n");
#endif
        CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
        if (logstream) {
            BIO_free(logstream);
            logstream = NULL;
        }
        if (CRYPTO_add(&bio->references, 1, CRYPTO_LOCK_BIO) > 1) {
            logstream = bio;
            ret = 1;
        } else {
            MSCRYPTOerr(MSCRYPTO_F_CTRL, MSCRYPTO_R_CANNOT_SET_LOGSTREAM);
        }
        CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
        } break;

    case CMD_MODULE_PATH:
#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_ctrl() CMD_MODULE_PATH\n");
#endif
        return(1);

    case CMD_STORE_NAME: {
#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_ctrl() CMD_STORE_NAME\n");
#endif
        CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
        ret = mscrypto_cmd_store_name((char *) p);
        CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
        } break;

    case CMD_PIN:
#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_ctrl() CMD_PIN\n");
#endif
        return(1);

    case CMD_QUIET:
#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_ctrl() CMD_QUIET\n");
#endif
	quiet_flag = !quiet_flag;
        return(1);

    default:
#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_ctrl() <UNKNOWN=%d>\n", cmd);
#endif
        break;
    }

    return(ret);
}


/*
 * RSA functions
 */
static int mscrypto_rsa_private_decrypt(
    int len,
    const unsigned char *from,
    unsigned char *to,
    RSA *rsa,
    int padding
);

static int mscrypto_rsa_private_encrypt(
    int len,
    const unsigned char *from,
    unsigned char *to,
    RSA *rsa,
    int padding
);

static int
    mscrypto_rsa_public_encrypt(
    int len,
    const unsigned char *from,
    unsigned char *to,
    RSA *rsa,
    int padding
);

static int
mscrypto_rsa_public_decrypt(
    int len,
    const unsigned char *from,
    unsigned char *to,
    RSA *rsa,
    int padding
);

static int
bnref_mod_exp(
    BIGNUM *r,
    const BIGNUM *a,
    const BIGNUM *p,
    const BIGNUM *m,
    BN_CTX *ctx,
    BN_MONT_CTX *m_ctx
);

static int
mscrypto_rsa_mod_exp(
    BIGNUM *r0,
    const BIGNUM *I,
    RSA *rsa
#if OPENSSL_VERSION_NUMBER >= 0x00908000L
    , BN_CTX *ctx
#endif
);

static int mscrypto_rsa_init(RSA *rsa);
static int mscrypto_rsa_finish(RSA *rsa);


/*
 * mscrypto RSA method
 */
static RSA_METHOD mscrypto_rsa = {
    "mscrypto pkcs#1 rsa",
    mscrypto_rsa_public_encrypt,
    mscrypto_rsa_public_decrypt,
    mscrypto_rsa_private_encrypt,
    mscrypto_rsa_private_decrypt,
    mscrypto_rsa_mod_exp,
    bnref_mod_exp,
    mscrypto_rsa_init,
    mscrypto_rsa_finish,
    /* int flags; */
      /* - enable rsa_sign and rsa_verify
         NOTE: don't work with rsautl !*/
    RSA_FLAG_SIGN_VER
      /* - rsa_mod_exp is called for private key operations, even if
         p,q,dmp1,dmq1,iqmp are NULL*/
    /*TODO?: | RSA_FLAG_EXT_PKEY (?)*/
      /* - don't check pub/private match*/
    | RSA_METHOD_FLAG_NO_CHECK
    ,
    NULL /* char *app_data; */,
    mscrypto_rsa_sign,
    mscrypto_rsa_verify
};



/*
 * Functions to handle the engine
 */

static int/*bool*/
bind_mscrypto(ENGINE *e) {
    if (!ENGINE_set_id(e, mscrypto_engine_id)
        || !ENGINE_set_name(e, mscrypto_engine_name)
        || !ENGINE_set_RSA(e, &mscrypto_rsa)
        /*|| !ENGINE_set_ciphers(e, mscrypto_ciphers)*/
        /*|| !ENGINE_set_digests(e, mscrypto_digests)*/
        || !ENGINE_set_destroy_function(e, mscrypto_destroy)
        || !ENGINE_set_init_function(e, mscrypto_init)
        || !ENGINE_set_finish_function(e, mscrypto_finish)
        || !ENGINE_set_ctrl_function(e, mscrypto_ctrl)
        || !ENGINE_set_load_privkey_function(e, mscrypto_load_privkey)
        /*|| !ENGINE_set_load_pubkey_function(e, mscrypto_load_pubkey)*/
        || !ENGINE_set_cmd_defns(e, mscrypto_cmd_defns)
    )
        return 0;

    /* ensure the mscrypto error handling is set up */
    ERR_load_MSCRYPTO_strings();
    return(1);
}

#if OPENSSL_VERSION_NUMBER >= 0x00908000L
  /* now definitions are in 0.9.8 engine style */
#else
  /*set dynamic/staic support defines in 0.9.8 style*/
#undef OPENSSL_NO_DYNAMIC_ENGINE
#undef OPENSSL_NO_STATIC_ENGINE

#ifdef ENGINE_DYNAMIC_SUPPORT
#  define OPENSSL_NO_STATIC_ENGINE
#else
#  define OPENSSL_NO_DYNAMIC_ENGINE
#endif

#endif


#ifndef OPENSSL_NO_DYNAMIC_ENGINE
static int/*bool*/
bind_helper(ENGINE *e, const char *id) {
#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] bind_fn(): id=%s\n", (id ? id: "<none>"));
#endif
    if (id && (strcmp(id, mscrypto_engine_id) != 0))
        return(0);
    return(bind_mscrypto(e));
}


IMPLEMENT_DYNAMIC_CHECK_FN()
IMPLEMENT_DYNAMIC_BIND_FN(bind_helper)
#endif /*ndef OPENSSL_NO_STATIC_ENGINE*/

#ifndef OPENSSL_NO_STATIC_ENGINE
static ENGINE *
engine_mscrypto(void) {
    ENGINE *e = ENGINE_new();
#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] engine_mscrypto()\n");
#endif
    if (!e)
        return(NULL);
    if (!bind_mscrypto(e)) {
        ENGINE_free(e);
        return(NULL);
    }
    return(e);
}


extern void ENGINE_load_mscrypto(void);
void
ENGINE_load_mscrypto(void) {
    /* copied from eng_[openssl|dyn].c */
    ENGINE *e = engine_mscrypto();
    if (!e) return;
    ENGINE_add(e);
    ENGINE_free(e);
    ERR_clear_error();
}
#endif /*ndef OPENSSL_NO_DYNAMIC_ENGINE*/


/* initiator which is only present to make sure this engine looks available */
static int/*bool*/
mscrypto_init(ENGINE *e) {
    int ret = 0;

    MSCRYPTO_CTX *ctx;

    const RSA_METHOD *def = RSA_get_default_method();
    UNUSED(def);

#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_init():\n");
#endif

    ctx = malloc(sizeof(MSCRYPTO_CTX));
    if (ctx == NULL) {
        MSCRYPTOerr(MSCRYPTO_F_INIT, MSCRYPTO_R_INSUFFICIENT_MEMORY);
        goto done;
    }
    memset(ctx, 0, sizeof(MSCRYPTO_CTX));
    ENGINE_set_ex_data(e, 0, ctx);

    if (!mscrypto_open_store(ctx)) {
        /*error code is already set*/
        goto done;
    }

    ret = 1;

done:
#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_init()^ ret=%d\n", ret);
#endif
    return(ret);
}


/* finisher which is only present to make sure this engine looks available */
static int
mscrypto_finish(ENGINE *e) {
    MSCRYPTO_CTX *ctx;

#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_finish():\n");
#endif

    ctx = ENGINE_get_ex_data(e, 0);
    if (ctx == NULL) {
        goto done;
    }
    ENGINE_set_ex_data(e, 0, NULL);

    mscrypto_close_store(ctx);

    free(ctx);
    ctx = NULL;

done:
#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_finish()^\n");
#endif
    return(1);
}


/* destructor (complements the "ENGINE_ncipher()" constructor) */
static int
mscrypto_destroy(ENGINE *e) {
    UNUSED(e);
#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_destroy()\n");
#endif
    ERR_unload_MSCRYPTO_strings();
    return(1);
}


static int/*bool*/
mscrypto_open_store(MSCRYPTO_CTX *ctx) {
    int ret = 0;

    if (ctx == NULL) {
        MSCRYPTOerr(MSCRYPTO_F_OPEN_STORE, MSCRYPTO_R_INVALID_ARGUMENT);
        goto done;
    }

    mscrypto_close_store(ctx);

    /*
     * Default system certificate store based on a subsystem protocol.
     *
     * Current examples of subsystems protocols are:
     *   "MY"    Cert Store hold certs with associated Private Keys
     *   "CA"    Certifying Authority certs
     *   "ROOT"  Root Certs
     *   "SPC"   Software publisher certs
     */
    ctx->hStoreHandle = CertOpenStore(
        (LPCSTR)CERT_STORE_PROV_SYSTEM,
        0,
        (HCRYPTPROV)NULL,
        CERT_SYSTEM_STORE_CURRENT_USER,
        (store_name ? store_name : L"MY")
    );
    if (ctx->hStoreHandle == NULL) {
        MSCRYPTOerr(MSCRYPTO_F_OPEN_STORE, MSCRYPTO_R_W32ERR);
        MSCRYPTOw32err("CertOpenStore fail");
        goto done;
    }

    ret = 1;

done:
    return(ret);
}


static void
mscrypto_close_store(MSCRYPTO_CTX *ctx) {
    if (ctx == NULL) {
        MSCRYPTOerr(MSCRYPTO_F_CLOSE_STORE, MSCRYPTO_R_INVALID_ARGUMENT);
        goto done;
    }

    if (ctx->hStoreHandle != NULL) {
        if (!CertCloseStore(ctx->hStoreHandle, CERT_CLOSE_STORE_CHECK_FLAG)) {
            MSCRYPTOerr(MSCRYPTO_F_CLOSE_STORE, MSCRYPTO_R_W32ERR);
            MSCRYPTOw32err("CertCloseStore fail");
        } else {
fprintf(stderr, "The store closed and all certificates are freed.\n");
        }
        ctx->hStoreHandle = NULL;
    }

done:
    {} /* work around for gcc 3.4.5 (or mingw special?) error:
          label at end of compound statement
       */
}


/*
 * RSA functions
 */

static int
mscrypto_rsa_mod_exp(
    BIGNUM *r0,
    const BIGNUM *I,
    RSA *rsa
#if OPENSSL_VERSION_NUMBER > 0x00908000L
    , BN_CTX *ctx
#endif
) {
    UNUSED(r0);
    UNUSED(I);
    UNUSED(rsa);
#if OPENSSL_VERSION_NUMBER > 0x00908000L
    UNUSED(ctx);
#endif
    MSCRYPTOerr(MSCRYPTO_F_MOD_EXP, MSCRYPTO_R_SHOULD_NOT_HAVE_BEEN_CALLED);
    return(0);
}


static int
bnref_mod_exp(
    BIGNUM *r,
    const BIGNUM *a,
    const BIGNUM *p,
    const BIGNUM *m,
    BN_CTX *ctx,
    BN_MONT_CTX *m_ctx
) {
    UNUSED(r);
    UNUSED(a);
    UNUSED(p);
    UNUSED(m);
    UNUSED(ctx);
    UNUSED(m_ctx);
    MSCRYPTOerr(MSCRYPTO_F_BNREF_MOD_EXP, MSCRYPTO_R_SHOULD_NOT_HAVE_BEEN_CALLED);
    return(0);
}


static int
mscrypto_rsa_init(RSA *rsa) {
    int ret = 0;
    MSCRYPTO_KEYCTX *keyctx;

#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_rsa_init():\n");
#endif

    keyctx = malloc(sizeof(MSCRYPTO_KEYCTX));
    if (keyctx == NULL) {
        MSCRYPTOerr(MSCRYPTO_F_RSA_INIT, MSCRYPTO_R_MISSING_KEY_CONTEXT);
        goto done;
    }
    memset(keyctx, 0, sizeof(MSCRYPTO_KEYCTX));
    RSA_set_ex_data(rsa, 0, keyctx);

    ret = 1;

done:
#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_rsa_init()^ ret=%d\n", ret);
#endif
    return(ret);
}


static int
mscrypto_rsa_finish(RSA *rsa) {
    MSCRYPTO_KEYCTX *keyctx;

#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_rsa_finish:\n");
#endif

    keyctx = RSA_get_ex_data(rsa, 0);
    if (keyctx == NULL) {
        MSCRYPTOerr(MSCRYPTO_F_RSA_FINISH, MSCRYPTO_R_MISSING_KEY_CONTEXT);
        goto done;
    }
    RSA_set_ex_data(rsa, 0, NULL);

    if (keyctx->hPubKey) {
        if (!CryptDestroyKey(keyctx->hPubKey)) {
            MSCRYPTOerr(MSCRYPTO_F_RSA_FINISH, MSCRYPTO_R_W32ERR);
            MSCRYPTOw32err("CryptDestroyKey fail for hPubKey");
        }
        keyctx->hPubKey = (HCRYPTKEY)NULL;
    }

    if (keyctx->hKey) {
        if (!CryptDestroyKey(keyctx->hKey)) {
            MSCRYPTOerr(MSCRYPTO_F_RSA_FINISH, MSCRYPTO_R_W32ERR);
            MSCRYPTOw32err("CryptDestroyKey fail for hKey");
        }
        keyctx->hKey = (HCRYPTKEY)NULL;
    }

    if (keyctx->pCert) {
        if (keyctx->hCryptProv && keyctx->fCallerFreeProv) {
            CryptReleaseContext(keyctx->hCryptProv, 0);
            keyctx->hCryptProv = (HCRYPTPROV)NULL;
        }
    } else {
        /*external key*/
        if (keyctx->hCryptProv) {
            CryptReleaseContext(keyctx->hCryptProv, 0);
            keyctx->hCryptProv = (HCRYPTPROV)NULL;
        }
    }

    if (keyctx->pCert) {
        if (!CertFreeCertificateContext(keyctx->pCert)){
            MSCRYPTOerr(MSCRYPTO_F_RSA_FINISH, MSCRYPTO_R_W32ERR);
            MSCRYPTOw32err("CertFreeCertificateContext fail");
        }
        keyctx->pCert = NULL;
    }

    free(keyctx);
    keyctx = NULL;

done:
#ifdef MSCRYPTO_TRACE
fprintf(stderr, "TRACE[mscrypto] mscrypto_rsa_finish^\n");
#endif
    return(1);
}


int/*bool*/
check_padding(int padding, const char* msg) {
    int ret = 1;

    switch (padding) {
    case RSA_PKCS1_PADDING: {
        fprintf(stderr, "%s: pkcs1 padding\n", msg);
    } break;
    case RSA_PKCS1_OAEP_PADDING: {
        fprintf(stderr, "%s: oaep padding\n", msg);
    } break;
    case RSA_SSLV23_PADDING: {
        fprintf(stderr, "%s: ssl padding\n", msg);
    } break;
    case RSA_NO_PADDING: {
        fprintf(stderr, "%s: raw padding\n", msg);
    } break;
    default: {
        fprintf(stderr, "%s: unknown padding(%d)\n", msg, padding);
        ret = 0;
    } break;
    }
    return(ret);
}



void*
memcpy_rev(void *dest, const void *src, size_t n) {
    char *p = dest;
    const char *q = src;

    q += n - 1;
    for ( ; n > 0; n--, p++, q--) {
        *p = *q;
    }
    return(dest);
};


static int
mscrypto_rsa_private_decrypt(
    int len,
    const unsigned char *from,
    unsigned char *to,
    RSA *rsa,
    int padding
) {
    return(mscrypto_rsa_decrypt(len, from, to, rsa, padding, 0));
}


static int
mscrypto_rsa_private_encrypt(
    int len,
    const unsigned char *from,
    unsigned char *to,
    RSA *rsa,
    int padding
) {
#if 0
    UNUSED(len);
    UNUSED(from);
    UNUSED(to);
    UNUSED(rsa);
fprintf(stderr, "mscrypto_rsa_private_encrypt(..., padding=%d) is not implemented\n", padding);
    return(0);
#else
    return(mscrypto_rsa_encrypt(len, from, to, rsa, padding, 0));
#endif
}


static int
mscrypto_rsa_public_decrypt(
    int len,
    const unsigned char *from,
    unsigned char *to,
    RSA *rsa,
    int padding
) {
#if 0
    UNUSED(len);
    UNUSED(from);
    UNUSED(to);
    UNUSED(rsa);
fprintf(stderr, "mscrypto_rsa_public_decrypt(..., padding=%d) is not implemented\n", padding);
    return(0);
#else
    return(mscrypto_rsa_decrypt(len, from, to, rsa, padding, 1));
#endif
}


static int
mscrypto_rsa_public_encrypt(
    int len,
    const unsigned char *from,
    unsigned char *to,
    RSA *rsa,
    int padding
) {
    return(mscrypto_rsa_encrypt(len, from, to, rsa, padding, 1));
}


void
MSCRYPTOw32err(const char *msg) {
    size_t msg_len = strlen(msg);

    DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM;
    DWORD dwMessageId = GetLastError();
    DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); /* Default language */

    char *err_buf;
    DWORD err_len;
    char *err_str = NULL;

    err_len = FormatMessage(
        dwFlags,
        (LPCVOID) NULL /*lpSource*/,
        dwMessageId,
        dwLanguageId,
        (LPTSTR) &err_buf,
        (DWORD) 0 /*nSize*/,
        (va_list*) NULL/*Arguments*/
    );
#if defined(UNICODE) || defined(_UNICODE)
/* Note with borland compiler flag -WU both are defined. */
#  error "UNICODE is not supported"
#endif

#if defined(MBCS) || defined(_MBCS)
#  error "MBCS is not tested"
#endif

    /*build error string*/
    err_str = OPENSSL_malloc(msg_len + 10 + 8 + 9 + err_len + 1 + 1);
    {
        char *p = err_str;
        char dw[8];

        strncpy(p, msg, msg_len);
        p += msg_len;

        strncpy(p, " - code=0x", 10);
        p += 10;

        snprintf(dw, 8, "%08lX", (long) dwMessageId);
        strncpy(p, dw, 8);
        p += 8;

        strncpy(p, " reason='", 9);
        p += 9;

        strncpy(p, err_buf, err_len);
        p += err_len;
        if (p[-1] == '\n') {
            p--;
            if (p[-1] == '\r') {
                p--;
            }
        }

        *p++ = '\'';
        *p = '\0';
    }
    ERR_set_error_data(err_str, ERR_TXT_MALLOCED | ERR_TXT_STRING);
}


#if OPENSSL_VERSION_NUMBER >= 0x00908000L
#include "mscrypto_rsa_decrypt.c"
#include "mscrypto_rsa_encrypt.c"
#include "mscrypto_rsa_sign.c"
#include "mscrypto_rsa_verify.c"
#include "kt_rsa.c"
#endif
