diff options
author | Dr. Stephen Henson <steve@openssl.org> | 2008-05-31 22:49:32 +0000 |
---|---|---|
committer | Dr. Stephen Henson <steve@openssl.org> | 2008-05-31 22:49:32 +0000 |
commit | 7a18ecb2df34baa6de462bfcf9379e38209d224b (patch) | |
tree | f30a605c5104da9366dc402efde8ccb00b99868e /engines/e_capi.c | |
parent | 174c86a216cadb650caef813a79cc1bb6ff75e7d (diff) |
Add CryptoAPI ENGINE from stable branch.
Diffstat (limited to 'engines/e_capi.c')
-rw-r--r-- | engines/e_capi.c | 1428 |
1 files changed, 1428 insertions, 0 deletions
diff --git a/engines/e_capi.c b/engines/e_capi.c new file mode 100644 index 0000000000..deea50c723 --- /dev/null +++ b/engines/e_capi.c @@ -0,0 +1,1428 @@ +/* engines/e_capi.c */ +/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL + * project. + */ +/* ==================================================================== + * Copyright (c) 2008 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + + +#include <stdio.h> +#include <string.h> +#include <openssl/crypto.h> +#include <openssl/buffer.h> +#include <openssl/engine.h> +#include <openssl/rsa.h> +#include <openssl/bn.h> +#include <openssl/pem.h> + +#ifdef OPENSSL_SYS_WIN32 +#ifndef OPENSSL_NO_CAPIENG + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x400 +#endif + +#include <windows.h> +#include <wincrypt.h> + +#include "e_capi_err.h" +#include "e_capi_err.c" + + +static const char *engine_capi_id = "capi"; +static const char *engine_capi_name = "CryptoAPI ENGINE"; + +typedef struct CAPI_CTX_st CAPI_CTX; +typedef struct CAPI_KEY_st CAPI_KEY; + +static void capi_addlasterror(void); +static void capi_adderror(DWORD err); + +static void CAPI_trace(CAPI_CTX *ctx, char *format, ...); + +static int capi_list_providers(CAPI_CTX *ctx, BIO *out); +static int capi_list_containers(CAPI_CTX *ctx, BIO *out); +int capi_list_certs(CAPI_CTX *ctx, BIO *out, char *storename); +void capi_free_key(CAPI_KEY *key); + +static PCCERT_CONTEXT capi_find_cert(CAPI_CTX *ctx, const char *id, HCERTSTORE hstore); + +CAPI_KEY *capi_find_key(CAPI_CTX *ctx, const char *id); + +static EVP_PKEY *capi_load_privkey(ENGINE *eng, const char *key_id, + UI_METHOD *ui_method, void *callback_data); +static int capi_rsa_sign(int dtype, const unsigned char *m, unsigned int m_len, + unsigned char *sigret, unsigned int *siglen, const RSA *rsa); +static int capi_rsa_priv_enc(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding); +static int capi_rsa_priv_dec(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding); +static int capi_rsa_free(RSA *rsa); + +static DSA_SIG *capi_dsa_do_sign(const unsigned char *digest, int dlen, + DSA *dsa); +static int capi_dsa_free(DSA *dsa); + +/* This structure contains CAPI ENGINE specific data: + * it contains various global options and affects how + * other functions behave. + */ + +#define CAPI_DBG_TRACE 2 +#define CAPI_DBG_ERROR 1 + +struct CAPI_CTX_st { + int debug_level; + char *debug_file; + /* Parameters to use for container lookup */ + DWORD keytype; + LPTSTR cspname; + DWORD csptype; + /* Certificate store name to use */ + LPTSTR storename; + +/* Lookup string meanings in load_private_key */ +/* Substring of subject: uses "storename" */ +#define CAPI_LU_SUBSTR 0 +/* Friendly name: uses storename */ +#define CAPI_LU_FNAME 1 +/* Container name: uses cspname, keytype */ +#define CAPI_LU_CONTNAME 2 + int lookup_method; +/* Info to dump with dumpcerts option */ +/* Issuer and serial name strings */ +#define CAPI_DMP_SUMMARY 0x1 +/* Friendly name */ +#define CAPI_DMP_FNAME 0x2 +/* Full X509_print dump */ +#define CAPI_DMP_FULL 0x4 +/* Dump PEM format certificate */ +#define CAPI_DMP_PEM 0x8 +/* Dump pseudo key (if possible) */ +#define CAPI_DMP_PSKEY 0x10 +/* Dump key info (if possible) */ +#define CAPI_DMP_PKEYINFO 0x20 + + DWORD dump_flags; +}; + + +static CAPI_CTX *capi_ctx_new(); +static void capi_ctx_free(CAPI_CTX *ctx); +static int capi_ctx_set_provname(CAPI_CTX *ctx, LPSTR pname, DWORD type, int check); +static int capi_ctx_set_provname_idx(CAPI_CTX *ctx, int idx); + +#define CAPI_CMD_LIST_CERTS ENGINE_CMD_BASE +#define CAPI_CMD_LOOKUP_CERT (ENGINE_CMD_BASE + 1) +#define CAPI_CMD_DEBUG_LEVEL (ENGINE_CMD_BASE + 2) +#define CAPI_CMD_DEBUG_FILE (ENGINE_CMD_BASE + 3) +#define CAPI_CMD_KEYTYPE (ENGINE_CMD_BASE + 4) +#define CAPI_CMD_LIST_CSPS (ENGINE_CMD_BASE + 5) +#define CAPI_CMD_SET_CSP_IDX (ENGINE_CMD_BASE + 6) +#define CAPI_CMD_SET_CSP_NAME (ENGINE_CMD_BASE + 7) +#define CAPI_CMD_SET_CSP_TYPE (ENGINE_CMD_BASE + 8) +#define CAPI_CMD_LIST_CONTAINERS (ENGINE_CMD_BASE + 9) +#define CAPI_CMD_LIST_OPTIONS (ENGINE_CMD_BASE + 10) +#define CAPI_CMD_LOOKUP_METHOD (ENGINE_CMD_BASE + 11) + +static const ENGINE_CMD_DEFN capi_cmd_defns[] = { + {CAPI_CMD_LIST_CERTS, + "list_certs", + "List all certificates in store", + ENGINE_CMD_FLAG_NO_INPUT}, + {CAPI_CMD_LOOKUP_CERT, + "lookup_cert", + "Lookup and output certificates", + ENGINE_CMD_FLAG_STRING}, + {CAPI_CMD_DEBUG_LEVEL, + "debug_level", + "debug level (1=errors, 2=trace)", + ENGINE_CMD_FLAG_NUMERIC}, + {CAPI_CMD_DEBUG_FILE, + "debug_file", + "debugging filename)", + ENGINE_CMD_FLAG_STRING}, + {CAPI_CMD_KEYTYPE, + "key_type", + "Key type: 1=AT_KEYEXCHANGE (default), 2=AT_SIGNATURE", + ENGINE_CMD_FLAG_NUMERIC}, + {CAPI_CMD_LIST_CSPS, + "list_csps", + "List all CSPs", + ENGINE_CMD_FLAG_NO_INPUT}, + {CAPI_CMD_SET_CSP_IDX, + "csp_idx", + "Set CSP by index", + ENGINE_CMD_FLAG_NUMERIC}, + {CAPI_CMD_SET_CSP_NAME, + "csp_name", + "Set CSP name, (default CSP used if not specified)", + ENGINE_CMD_FLAG_STRING}, + {CAPI_CMD_SET_CSP_TYPE, + "csp_type", + "Set CSP type, (default RSA_PROV_FULL)", + ENGINE_CMD_FLAG_NUMERIC}, + {CAPI_CMD_LIST_CONTAINERS, + "list_containers", + "list container names", + ENGINE_CMD_FLAG_NO_INPUT}, + {CAPI_CMD_LIST_OPTIONS, + "list_options", + "Set list options (1=summary,2=friendly name, 4=full printout, 8=PEM output, 16=XXX, " + "32=private key info)", + ENGINE_CMD_FLAG_NUMERIC}, + {CAPI_CMD_LOOKUP_METHOD, + "lookup_method", + "Set key lookup method (1=substring, 2=friendlyname, 3=container name)", + ENGINE_CMD_FLAG_NUMERIC}, + + {0, NULL, NULL, 0} + }; + +static int capi_idx = -1; +static int rsa_capi_idx = -1; +static int dsa_capi_idx = -1; + +static int capi_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void)) +{ + int ret = 1; + CAPI_CTX *ctx; + BIO *out; + if (capi_idx == -1) + { + CAPIerr(CAPI_F_CAPI_CTRL, CAPI_R_ENGINE_NOT_INITIALIZED); + return 0; + } + ctx = ENGINE_get_ex_data(e, capi_idx); + out = BIO_new_fp(stdout, BIO_NOCLOSE); + switch (cmd) + { + case CAPI_CMD_LIST_CSPS: + ret = capi_list_providers(ctx, out); + break; + + case CAPI_CMD_LIST_CERTS: + ret = capi_list_certs(ctx, out, NULL); + break; + + case CAPI_CMD_LOOKUP_CERT: + ret = capi_list_certs(ctx, out, p); + break; + + case CAPI_CMD_LIST_CONTAINERS: + ret = capi_list_containers(ctx, out); + break; + + case CAPI_CMD_DEBUG_LEVEL: + ctx->debug_level = (int)i; + CAPI_trace(ctx, "Setting debug level to %d\n", ctx->debug_level); + break; + + case CAPI_CMD_DEBUG_FILE: + ctx->debug_file = BUF_strdup(p); + CAPI_trace(ctx, "Setting debug file to %s\n", ctx->debug_file); + break; + + case CAPI_CMD_KEYTYPE: + ctx->keytype = i; + CAPI_trace(ctx, "Setting key type to %d\n", ctx->keytype); + break; + + case CAPI_CMD_SET_CSP_IDX: + ret = capi_ctx_set_provname_idx(ctx, i); + break; + + case CAPI_CMD_LIST_OPTIONS: + ctx->dump_flags = i; + break; + + case CAPI_CMD_LOOKUP_METHOD: + if (i < 1 || i > 3) + { + CAPIerr(CAPI_F_CAPI_CTRL, CAPI_R_INVALID_LOOKUP_METHOD); + return 0; + } + ctx->lookup_method = i; + break; + + case CAPI_CMD_SET_CSP_NAME: + ret = capi_ctx_set_provname(ctx, p, ctx->csptype, 1); + break; + + case CAPI_CMD_SET_CSP_TYPE: + ctx->csptype = i; + break; + + default: + CAPIerr(CAPI_F_CAPI_CTRL, CAPI_R_UNKNOWN_COMMAND); + ret = 0; + } + + BIO_free(out); + return ret; + +} + +static RSA_METHOD capi_rsa_method = + { + "CryptoAPI RSA method", + 0, /* pub_enc */ + 0, /* pub_dec */ + capi_rsa_priv_enc, /* priv_enc */ + capi_rsa_priv_dec, /* priv_dec */ + 0, /* rsa_mod_exp */ + 0, /* bn_mod_exp */ + 0, /* init */ + capi_rsa_free, /* finish */ + RSA_FLAG_SIGN_VER, /* flags */ + NULL, /* app_data */ + capi_rsa_sign, /* rsa_sign */ + 0 /* rsa_verify */ + }; + +static DSA_METHOD capi_dsa_method = + { + "CryptoAPI DSA method", + capi_dsa_do_sign, /* dsa_do_sign */ + 0, /* dsa_sign_setup */ + 0, /* dsa_do_verify */ + 0, /* dsa_mod_exp */ + 0, /* bn_mod_exp */ + 0, /* init */ + capi_dsa_free, /* finish */ + 0, /* flags */ + NULL, /* app_data */ + 0, /* dsa_paramgen */ + 0 /* dsa_keygen */ + }; + +static int capi_init(ENGINE *e) + { + CAPI_CTX *ctx; + const RSA_METHOD *ossl_rsa_meth; + const DSA_METHOD *ossl_dsa_meth; + capi_idx = ENGINE_get_ex_new_index(0, NULL, NULL, NULL, 0); + + ctx = capi_ctx_new(); + if (!ctx || (capi_idx < 0)) + goto memerr; + + ENGINE_set_ex_data(e, capi_idx, ctx); + /* Setup RSA_METHOD */ + rsa_capi_idx = RSA_get_ex_new_index(0, NULL, NULL, NULL, 0); + ossl_rsa_meth = RSA_PKCS1_SSLeay(); + capi_rsa_method.rsa_pub_enc = ossl_rsa_meth->rsa_pub_enc; + capi_rsa_method.rsa_pub_dec = ossl_rsa_meth->rsa_pub_dec; + capi_rsa_method.rsa_mod_exp = ossl_rsa_meth->rsa_mod_exp; + capi_rsa_method.bn_mod_exp = ossl_rsa_meth->bn_mod_exp; + + /* Setup DSA Method */ + dsa_capi_idx = DSA_get_ex_new_index(0, NULL, NULL, NULL, 0); + ossl_dsa_meth = DSA_OpenSSL(); + capi_dsa_method.dsa_do_verify = ossl_dsa_meth->dsa_do_verify; + capi_dsa_method.dsa_mod_exp = ossl_dsa_meth->dsa_mod_exp; + capi_dsa_method.bn_mod_exp = ossl_dsa_meth->bn_mod_exp; + + return 1; + + memerr: + CAPIerr(CAPI_F_CAPI_INIT, ERR_R_MALLOC_FAILURE); + return 0; + + return 1; + } + +static int capi_destroy(ENGINE *e) + { + + ERR_unload_CAPI_strings(); + return 1; + } + +static int capi_finish(ENGINE *e) + { + CAPI_CTX *ctx; + ctx = ENGINE_get_ex_data(e, capi_idx); + capi_ctx_free(ctx); + ENGINE_set_ex_data(e, capi_idx, NULL); + return 1; + } + + +/* CryptoAPI key application data. This contains + * a handle to the private key container (for sign operations) + * and a handle to the key (for decrypt operations). + */ + +struct CAPI_KEY_st + { + HCRYPTPROV hprov; + HCRYPTKEY key; + }; + +static int bind_capi(ENGINE *e) + { + if (!ENGINE_set_id(e, engine_capi_id) + || !ENGINE_set_name(e, engine_capi_name) + || !ENGINE_set_init_function(e, capi_init) + || !ENGINE_set_finish_function(e, capi_finish) + || !ENGINE_set_destroy_function(e, capi_destroy) + || !ENGINE_set_RSA(e, &capi_rsa_method) + || !ENGINE_set_DSA(e, &capi_dsa_method) + || !ENGINE_set_load_privkey_function(e, capi_load_privkey) + || !ENGINE_set_cmd_defns(e, capi_cmd_defns) + || !ENGINE_set_ctrl_function(e, capi_ctrl)) + return 0; + ERR_load_CAPI_strings(); + + return 1; + + } + +#ifndef OPENSSL_NO_DYNAMIC_ENGINE +static int bind_helper(ENGINE *e, const char *id) + { + if(id && (strcmp(id, engine_capi_id) != 0)) + return 0; + if(!bind_capi(e)) + return 0; + return 1; + } +IMPLEMENT_DYNAMIC_CHECK_FN() +IMPLEMENT_DYNAMIC_BIND_FN(bind_helper) +#else +static ENGINE *engine_capi(void) + { + ENGINE *ret = ENGINE_new(); + if(!ret) + return NULL; + if(!bind_capi(ret)) + { + ENGINE_free(ret); + return NULL; + } + return ret; + } + +void ENGINE_load_capi(void) + { + /* Copied from eng_[openssl|dyn].c */ + ENGINE *toadd = engine_capi(); + if(!toadd) return; + ENGINE_add(toadd); + ENGINE_free(toadd); + ERR_clear_error(); + } +#endif + + +static int lend_tobn(BIGNUM *bn, unsigned char *bin, int binlen) + { + int i; + /* Reverse buffer in place: since this is a keyblob structure + * that will be freed up after conversion anyway it doesn't + * matter if we change it. + */ + for(i = 0; i < binlen / 2; i++) + { + unsigned char c; + c = bin[i]; + bin[i] = bin[binlen - i - 1]; + bin[binlen - i - 1] = c; + } + + if (!BN_bin2bn(bin, binlen, bn)) + return 0; + return 1; + } + +static EVP_PKEY *capi_load_privkey(ENGINE *eng, const char *key_id, + UI_METHOD *ui_method, void *callback_data) + { + EVP_PKEY *ret = NULL; + CAPI_CTX *ctx; + CAPI_KEY *key; + unsigned char *pubkey = NULL; + DWORD len; + BLOBHEADER *bh; + RSA *rkey = NULL; + DSA *dkey = NULL; + ctx = ENGINE_get_ex_data(eng, capi_idx); + + if (!ctx) + { + CAPIerr(CAPI_F_CAPI_LOAD_PRIVKEY, CAPI_R_CANT_FIND_CAPI_CONTEXT); + return NULL; + } + + key = capi_find_key(ctx, key_id); + + if (!key) + return NULL; + + len = 0; + if (!CryptExportKey(key->key, 0, PUBLICKEYBLOB, 0, NULL, &len)) + { + CAPIerr(CAPI_F_CAPI_LOAD_PRIVKEY, CAPI_R_PUBKEY_EXPORT_LENGTH_ERROR); + capi_addlasterror(); + return NULL; + } + + pubkey = OPENSSL_malloc(len); + + if (!pubkey) + goto memerr; + + if (!CryptExportKey(key->key, 0, PUBLICKEYBLOB, 0, pubkey, &len)) + { + CAPIerr(CAPI_F_CAPI_LOAD_PRIVKEY, CAPI_R_PUBKEY_EXPORT_ERROR); + capi_addlasterror(); + goto err; + } + + bh = (BLOBHEADER *)pubkey; + if (bh->bType != PUBLICKEYBLOB) + { + CAPIerr(CAPI_F_CAPI_LOAD_PRIVKEY, CAPI_R_INVALID_PUBLIC_KEY_BLOB); + goto err; + } + if (bh->aiKeyAlg == CALG_RSA_SIGN || bh->aiKeyAlg == CALG_RSA_KEYX) + { + RSAPUBKEY *rp; + DWORD rsa_modlen; + unsigned char *rsa_modulus; + rp = (RSAPUBKEY *)(bh + 1); + if (rp->magic != 0x31415352) + { + char magstr[10]; + BIO_snprintf(magstr, 10, "%lx", rp->magic); + CAPIerr(CAPI_F_CAPI_LOAD_PRIVKEY, CAPI_R_INVALID_RSA_PUBLIC_KEY_BLOB_MAGIC_NUMBER); + ERR_add_error_data(2, "magic=0x", magstr); + goto err; + } + rsa_modulus = (unsigned char *)(rp + 1); + rkey = RSA_new_method(eng); + if (!rkey) + goto memerr; + + rkey->e = BN_new(); + rkey->n = BN_new(); + + if (!rkey->e || !rkey->n) + goto memerr; + + if (!BN_set_word(rkey->e, rp->pubexp)) + goto memerr; + + rsa_modlen = rp->bitlen / 8; + if (!lend_tobn(rkey->n, rsa_modulus, rsa_modlen)) + goto memerr; + + RSA_set_ex_data(rkey, rsa_capi_idx, key); + + if (!(ret = EVP_PKEY_new())) + goto memerr; + + EVP_PKEY_assign_RSA(ret, rkey); + rkey = NULL; + + } + else if (bh->aiKeyAlg == CALG_DSS_SIGN) + { + DSSPUBKEY *dp; + DWORD dsa_plen; + unsigned char *btmp; + dp = (DSSPUBKEY *)(bh + 1); + if (dp->magic != 0x31535344) + { + char magstr[10]; + BIO_snprintf(magstr, 10, "%lx", dp->magic); + CAPIerr(CAPI_F_CAPI_LOAD_PRIVKEY, CAPI_R_INVALID_DSA_PUBLIC_KEY_BLOB_MAGIC_NUMBER); + ERR_add_error_data(2, "magic=0x", magstr); + goto err; + } + dsa_plen = dp->bitlen / 8; + btmp = (unsigned char *)(dp + 1); + dkey = DSA_new_method(eng); + if (!dkey) + goto memerr; + dkey->p = BN_new(); + dkey->q = BN_new(); + dkey->g = BN_new(); + dkey->pub_key = BN_new(); + if (!dkey->p || !dkey->q || !dkey->g || !dkey->pub_key) + goto memerr; + if (!lend_tobn(dkey->p, btmp, dsa_plen)) + goto memerr; + btmp += dsa_plen; + if (!lend_tobn(dkey->q, btmp, 20)) + goto memerr; + btmp += 20; + if (!lend_tobn(dkey->g, btmp, dsa_plen)) + goto memerr; + btmp += dsa_plen; + if (!lend_tobn(dkey->pub_key, btmp, dsa_plen)) + goto memerr; + btmp += dsa_plen; + + DSA_set_ex_data(dkey, dsa_capi_idx, key); + + if (!(ret = EVP_PKEY_new())) + goto memerr; + + EVP_PKEY_assign_DSA(ret, dkey); + dkey = NULL; + } + else + { + char algstr[10]; + BIO_snprintf(algstr, 10, "%lx", bh->aiKeyAlg); + CAPIerr(CAPI_F_CAPI_LOAD_PRIVKEY, CAPI_R_UNSUPPORTED_PUBLIC_KEY_ALGORITHM); + ERR_add_error_data(2, "aiKeyAlg=0x", algstr); + goto err; + } + + err: + if (pubkey) + OPENSSL_free(pubkey); + if (!ret) + { + if (rkey) + RSA_free(rkey); + if (dkey) + DSA_free(dkey); + if (key) + capi_free_key(key); + } + + return ret; + +memerr: + CAPIerr(CAPI_F_CAPI_LOAD_PRIVKEY, ERR_R_MALLOC_FAILURE); + goto err; + + } + +/* CryptoAPI RSA operations */ + +int capi_rsa_priv_enc(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) + { + CAPIerr(CAPI_F_CAPI_RSA_PRIV_ENC, CAPI_R_FUNCTION_NOT_SUPPORTED); + return -1; + } + +int capi_rsa_sign(int dtype, const unsigned char *m, unsigned int m_len, + unsigned char *sigret, unsigned int *siglen, const RSA *rsa) + { + ALG_ID alg; + HCRYPTHASH hash; + DWORD slen; + unsigned int i; + int ret = -1; + CAPI_KEY *capi_key; + CAPI_CTX *ctx; + + ctx = ENGINE_get_ex_data(rsa->engine, capi_idx); + + CAPI_trace(ctx, "Called CAPI_rsa_sign()\n"); + + capi_key = RSA_get_ex_data(rsa, rsa_capi_idx); + if (!capi_key) + { + CAPIerr(CAPI_F_CAPI_RSA_SIGN, CAPI_R_CANT_GET_KEY); + return -1; + } +/* Convert the signature type to a CryptoAPI algorithm ID */ + switch(dtype) { + case NID_sha1: + alg = CALG_SHA1; + break; + + case NID_md5: + alg = CALG_MD5; + break; + + case NID_md5_sha1: + alg = CALG_SSL3_SHAMD5; + break; + default: + { + char algstr[10]; + BIO_snprintf(algstr, 10, "%lx", dtype); + CAPIerr(CAPI_F_CAPI_RSA_SIGN, CAPI_R_UNSUPPORTED_ALGORITHM_NID); + ERR_add_error_data(2, "NID=0x", algstr); + return -1; + } + } + + + +/* Create the hash object */ + if(!CryptCreateHash(capi_key->hprov, alg, 0, 0, &hash)) { + CAPIerr(CAPI_F_CAPI_RSA_SIGN, CAPI_R_CANT_CREATE_HASH_OBJECT); + capi_addlasterror(); + return -1; + } +/* Set the hash value to the value passed */ + + if(!CryptSetHashParam(hash, HP_HASHVAL, (unsigned char *)m, 0)) { + CAPIerr(CAPI_F_CAPI_RSA_SIGN, CAPI_R_CANT_SET_HASH_VALUE); + capi_addlasterror(); + goto err; + } + + +/* Finally sign it */ + slen = RSA_size(rsa); + if(!CryptSignHash(hash, AT_KEYEXCHANGE, NULL, 0, sigret, &slen)) { + CAPIerr(CAPI_F_CAPI_RSA_SIGN, CAPI_R_ERROR_SIGNING_HASH); + capi_addlasterror(); + goto err; + } else { + ret = 1; + /* Inplace byte reversal of signature */ + for(i = 0; i < slen / 2; i++) { + unsigned char c; + c = sigret[i]; + sigret[i] = sigret[slen - i - 1]; + sigret[slen - i - 1] = c; + } + *siglen = slen; + } + + + /* Now cleanup */ + +err: + CryptDestroyHash(hash); + + return ret; +} + +int capi_rsa_priv_dec(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + int i; + unsigned char *tmpbuf; + CAPI_KEY *capi_key; + CAPI_CTX *ctx; + ctx = ENGINE_get_ex_data(rsa->engine, capi_idx); + + CAPI_trace(ctx, "Called capi_rsa_priv_dec()\n"); + + + capi_key = RSA_get_ex_data(rsa, rsa_capi_idx); + if (!capi_key) + { + CAPIerr(CAPI_F_CAPI_RSA_PRIV_DEC, CAPI_R_CANT_GET_KEY); + return -1; + } + + if(padding != RSA_PKCS1_PADDING) + { + char errstr[10]; + BIO_snprintf(errstr, 10, "%d", padding); + CAPIerr(CAPI_F_CAPI_RSA_PRIV_DEC, CAPI_R_UNSUPPORTED_PADDING); + ERR_add_error_data(2, "padding=", errstr); + return -1; + } + + /* Create temp reverse order version of input */ + if(!(tmpbuf = OPENSSL_malloc(flen)) ) + { + CAPIerr(CAPI_F_CAPI_RSA_PRIV_DEC, ERR_R_MALLOC_FAILURE); + return -1; + } + for(i = 0; i < flen; i++) + tmpbuf[flen - i - 1] = from[i]; + + /* Finally decrypt it */ + if(!CryptDecrypt(capi_key->key, 0, TRUE, 0, tmpbuf, &flen)) + { + CAPIerr(CAPI_F_CAPI_RSA_PRIV_DEC, CAPI_R_DECRYPT_ERROR); + capi_addlasterror(); + OPENSSL_free(tmpbuf); + return -1; + } + else memcpy(to, tmpbuf, flen); + + OPENSSL_free(tmpbuf); + + return flen; +} + +static int capi_rsa_free(RSA *rsa) + { + CAPI_KEY *capi_key; + capi_key = RSA_get_ex_data(rsa, rsa_capi_idx); + capi_free_key(capi_key); + RSA_set_ex_data(rsa, rsa_capi_idx, 0); + return 1; + } + +/* CryptoAPI DSA operations */ + +static DSA_SIG *capi_dsa_do_sign(const unsigned char *digest, int dlen, + DSA *dsa) + { + HCRYPTHASH hash; + DWORD slen; + DSA_SIG *ret = NULL; + CAPI_KEY *capi_key; + CAPI_CTX *ctx; + unsigned char csigbuf[40]; + + ctx = ENGINE_get_ex_data(dsa->engine, capi_idx); + + CAPI_trace(ctx, "Called CAPI_dsa_do_sign()\n"); + + capi_key = DSA_get_ex_data(dsa, dsa_capi_idx); + + if (!capi_key) + { + CAPIerr(CAPI_F_CAPI_DSA_DO_SIGN, CAPI_R_CANT_GET_KEY); + return NULL; + } + + if (dlen != 20) + { + CAPIerr(CAPI_F_CAPI_DSA_DO_SIGN, CAPI_R_INVALID_DIGEST_LENGTH); + return NULL; + } + + /* Create the hash object */ + if(!CryptCreateHash(capi_key->hprov, CALG_SHA1, 0, 0, &hash)) { + CAPIerr(CAPI_F_CAPI_DSA_DO_SIGN, CAPI_R_CANT_CREATE_HASH_OBJECT); + capi_addlasterror(); + return NULL; + } + + /* Set the hash value to the value passed */ + if(!CryptSetHashParam(hash, HP_HASHVAL, (unsigned char *)digest, 0)) { + CAPIerr(CAPI_F_CAPI_DSA_DO_SIGN, CAPI_R_CANT_SET_HASH_VALUE); + capi_addlasterror(); + goto err; + } + + + /* Finally sign it */ + slen = sizeof(csigbuf); + if(!CryptSignHash(hash, AT_SIGNATURE, NULL, 0, csigbuf, &slen)) + { + CAPIerr(CAPI_F_CAPI_DSA_DO_SIGN, CAPI_R_ERROR_SIGNING_HASH); + capi_addlasterror(); + goto err; + } + else + { + ret = DSA_SIG_new(); + if (!ret) + goto err; + ret->r = BN_new(); + ret->s = BN_new(); + if (!ret->r || !ret->s) + goto err; + if (!lend_tobn(ret->r, csigbuf, 20) + || !lend_tobn(ret->s, csigbuf + 20, 20)) + { + DSA_SIG_free(ret); + ret = NULL; + goto err; + } + } + + /* Now cleanup */ + +err: + OPENSSL_cleanse(csigbuf, 40); + CryptDestroyHash(hash); + return ret; + } + +static int capi_dsa_free(DSA *dsa) + { + CAPI_KEY *capi_key; + capi_key = DSA_get_ex_data(dsa, dsa_capi_idx); + capi_free_key(capi_key); + DSA_set_ex_data(dsa, dsa_capi_idx, 0); + return 1; + } + +static void capi_vtrace(CAPI_CTX *ctx, int level, char *format, va_list argptr) + { + BIO *out; + + if (!ctx || (ctx->debug_level < level) || (!ctx->debug_file)) + return; + out = BIO_new_file(ctx->debug_file, "a+"); + BIO_vprintf(out, format, argptr); + BIO_free(out); + } + +static void CAPI_trace(CAPI_CTX *ctx, char *format, ...) + { + va_list args; + va_start(args, format); + capi_vtrace(ctx, CAPI_DBG_TRACE, format, args); + va_end(args); + } + +static void capi_addlasterror(void) + { + capi_adderror(GetLastError()); + } + +static void capi_adderror(DWORD err) + { + char errstr[10]; + BIO_snprintf(errstr, 10, "%lX", err); + ERR_add_error_data(2, "Error code= 0x", errstr); + } + +static char *wide_to_asc(LPWSTR wstr) + { + char *str; + if (!wstr) + return NULL; + str = OPENSSL_malloc(wcslen(wstr) + 1); + if (!str) + { + CAPIerr(CAPI_F_WIDE_TO_ASC, ERR_R_MALLOC_FAILURE); + return NULL; + } + sprintf(str, "%S", wstr); + return str; + } + +static int capi_get_provname(CAPI_CTX *ctx, LPSTR *pname, DWORD *ptype, DWORD idx) + { + LPSTR name; + DWORD len, err; + CAPI_trace(ctx, "capi_get_provname, index=%d\n", idx); + if (!CryptEnumProviders(idx, NULL, 0, ptype, NULL, &len)) + { + err = GetLastError(); + if (err == ERROR_NO_MORE_ITEMS) + return 2; + CAPIerr(CAPI_F_CAPI_GET_PROVNAME, CAPI_R_CRYPTENUMPROVIDERS_ERROR); + capi_adderror(err); + return 0; + } + name = OPENSSL_malloc(len); + if (!CryptEnumProviders(idx, NULL, 0, ptype, name, &len)) + { + err = GetLastError(); + if (err == ERROR_NO_MORE_ITEMS) + return 2; + CAPIerr(CAPI_F_CAPI_GET_PROVNAME, CAPI_R_CRYPTENUMPROVIDERS_ERROR); + capi_adderror(err); + return 0; + } + *pname = name; + CAPI_trace(ctx, "capi_get_provname, returned name=%s, type=%d\n", name, *ptype); + + return 1; + } + +static int capi_list_providers(CAPI_CTX *ctx, BIO *out) + { + DWORD idx, ptype; + int ret; + LPTSTR provname = NULL; + CAPI_trace(ctx, "capi_list_providers\n"); + BIO_printf(out, "Available CSPs:\n"); + for(idx = 0; ; idx++) + { + ret = capi_get_provname(ctx, &provname, &ptype, idx); + if (ret == 2) + break; + if (ret == 0) + break; + BIO_printf(out, "%d. %s, type %d\n", idx, provname, ptype); + OPENSSL_free(provname); + } + return 1; + } + +static int capi_list_containers(CAPI_CTX *ctx, BIO *out) + { + int ret = 1; + HCRYPTPROV hprov; + DWORD err, idx, flags, buflen = 0, clen; + LPSTR cname; + CAPI_trace(ctx, "Listing containers CSP=%s, type = %d\n", ctx->cspname, ctx->csptype); + if (!CryptAcquireContext(&hprov, NULL, ctx->cspname, ctx->csptype, CRYPT_VERIFYCONTEXT)) + { + CAPIerr(CAPI_F_CAPI_LIST_CONTAINERS, CAPI_R_CRYPTACQUIRECONTEXT_ERROR); + capi_addlasterror(); + return 0; + } + if (!CryptGetProvParam(hprov, PP_ENUMCONTAINERS, NULL, &buflen, CRYPT_FIRST)) + { + CAPIerr(CAPI_F_CAPI_LIST_CONTAINERS, CAPI_R_ENUMCONTAINERS_ERROR); + capi_addlasterror(); + return 0; + } + CAPI_trace(ctx, "Got max container len %d\n", buflen); + if (buflen == 0) + buflen = 1024; + cname = OPENSSL_malloc(buflen); + if (!cname) + { + CAPIerr(CAPI_F_CAPI_LIST_CONTAINERS, ERR_R_MALLOC_FAILURE); + goto err; + } + + for (idx = 0;;idx++) + { + clen = buflen; + cname[0] = 0; + + if (idx == 0) + flags = CRYPT_FIRST; + else + flags = 0; + if(!CryptGetProvParam(hprov, PP_ENUMCONTAINERS, cname, &clen, flags)) + { + err = GetLastError(); + if (err == ERROR_NO_MORE_ITEMS) + goto done; + CAPIerr(CAPI_F_CAPI_LIST_CONTAINERS, CAPI_R_ENUMCONTAINERS_ERROR); + capi_adderror(err); + goto err; + } + CAPI_trace(ctx, "Container name %s, len=%d, index=%d, flags=%d\n", cname, clen, idx, flags); + if (!cname[0] && (clen == buflen)) + { + CAPI_trace(ctx, "Enumerate bug: using workaround\n"); + goto done; + } + BIO_printf(out, "%d. %s\n", idx, cname); + } + err: + + ret = 0; + + done: + if (cname) + OPENSSL_free(cname); + CryptReleaseContext(hprov, 0); + + return ret; + } + +CRYPT_KEY_PROV_INFO *capi_get_prov_info(CAPI_CTX *ctx, PCCERT_CONTEXT cert) + { + DWORD len; + CRYPT_KEY_PROV_INFO *pinfo; + + if(!CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, NULL, &len)) + return NULL; + pinfo = OPENSSL_malloc(len); + if (!pinfo) + { + CAPIerr(CAPI_F_CAPI_GET_PROV_INFO, ERR_R_MALLOC_FAILURE); + return NULL; + } + if(!CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, pinfo, &len)) + { + CAPIerr(CAPI_F_CAPI_GET_PROV_INFO, CAPI_R_ERROR_GETTING_KEY_PROVIDER_INFO); + capi_addlasterror(); + OPENSSL_free(pinfo); + return NULL; + } + return pinfo; + } + +static void capi_dump_prov_info(CAPI_CTX *ctx, BIO *out, CRYPT_KEY_PROV_INFO *pinfo) + { + char *provname = NULL, *contname = NULL; + if (!pinfo) + { + BIO_printf(out, " No Private Key\n"); + return; + } + provname = wide_to_asc(pinfo->pwszProvName); + contname = wide_to_asc(pinfo->pwszContainerName); + if (!provname || !contname) + goto err; + + BIO_printf(out, " Private Key Info:\n"); + BIO_printf(out, " Provider Name: %s, Provider Type %d\n", provname, pinfo->dwProvType); + BIO_printf(out, " Container Name: %s, Key Type %d\n", contname, pinfo->dwKeySpec); + err: + if (provname) + OPENSSL_free(provname); + if (contname) + OPENSSL_free(contname); + } + +char * capi_cert_get_fname(CAPI_CTX *ctx, PCCERT_CONTEXT cert) + { + LPWSTR wfname; + DWORD dlen; + + CAPI_trace(ctx, "capi_cert_get_fname\n"); + if (!CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, NULL, &dlen)) + return NULL; + wfname = OPENSSL_malloc(dlen); + if (CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, wfname, &dlen)) + { + char *fname = wide_to_asc(wfname); + OPENSSL_free(wfname); + return fname; + } + CAPIerr(CAPI_F_CAPI_CERT_GET_FNAME, CAPI_R_ERROR_GETTING_FRIENDLY_NAME); + capi_addlasterror(); + + OPENSSL_free(wfname); + return NULL; + } + + +void capi_dump_cert(CAPI_CTX *ctx, BIO *out, PCCERT_CONTEXT cert) + { + X509 *x; + unsigned char *p; + unsigned long flags = ctx->dump_flags; + if (flags & CAPI_DMP_FNAME) + { + char *fname; + fname = capi_cert_get_fname(ctx, cert); + if (fname) + { + BIO_printf(out, " Friendly Name \"%s\"\n", fname); + OPENSSL_free(fname); + } + else + BIO_printf(out, " <No Friendly Name>\n"); + } + + p = cert->pbCertEncoded; + x = d2i_X509(NULL, &p, cert->cbCertEncoded); + if (!x) + BIO_printf(out, " <Can't parse certificate>\n"); + if (flags & CAPI_DMP_SUMMARY) + { + BIO_printf(out, " Subject: "); + X509_NAME_print_ex(out, X509_get_subject_name(x), 0, XN_FLAG_ONELINE); + BIO_printf(out, "\n Issuer: "); + X509_NAME_print_ex(out, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE); + BIO_printf(out, "\n"); + } + if (flags & CAPI_DMP_FULL) + X509_print_ex(out, x, XN_FLAG_ONELINE,0); + + if (flags & CAPI_DMP_PKEYINFO) + { + CRYPT_KEY_PROV_INFO *pinfo; + pinfo = capi_get_prov_info(ctx, cert); + capi_dump_prov_info(ctx, out, pinfo); + if (pinfo) + OPENSSL_free(pinfo); + } + + if (flags & CAPI_DMP_PEM) + PEM_write_bio_X509(out, x); |