From e062f620f4fd888d285f1b619852283291fbb06f Mon Sep 17 00:00:00 2001 From: Christian Hohnstaedt Date: Sun, 9 Feb 2025 11:19:59 +0100 Subject: [PATCH] WIP: Extract ENGINE / PROVIDER functionality --- lib/CMakeLists.txt | 3 +- lib/pkcs11.cpp | 377 ++------------------------ lib/pkcs11_provider.cpp | 579 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 600 insertions(+), 359 deletions(-) create mode 100644 lib/pkcs11_provider.cpp diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 090303b7..6a85d780 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -39,9 +39,8 @@ XcaProgress.h XcaWarningCore.cpp XcaWarningCore.h PwDialogCore.cpp PwDialogCore.h digest.h digest.cpp pki_export.cpp debug_info.h func_base.cpp func_base.h debug_info.cpp -cmdline.cpp +cmdline.cpp pkcs11_provider.cpp ) - macro(ExpandSources target) list(TRANSFORM ${target}_sources PREPEND ${PROJECT_SOURCE_DIR}/lib/) list(APPEND ${target} ${PROJECT_BINARY_DIR}/local.h) diff --git a/lib/pkcs11.cpp b/lib/pkcs11.cpp index b9f27d8c..43efd29d 100644 --- a/lib/pkcs11.cpp +++ b/lib/pkcs11.cpp @@ -649,376 +649,39 @@ int pkcs11::encrypt(int flen, const unsigned char *from, return size; } -static int rsa_privdata_free(RSA *rsa) -{ - pkcs11 *priv = (pkcs11*)RSA_get_app_data(rsa); - delete priv; - return 0; -} +#include +#include -static int rsa_encrypt(int flen, const unsigned char *from, - unsigned char *to, RSA * rsa, int padding) -{ - pkcs11 *priv = (pkcs11*)RSA_get_app_data(rsa); - const BIGNUM *n = NULL; - - if (padding != RSA_PKCS1_PADDING) { - return -1; - } - RSA_get0_key(rsa, &n, NULL, NULL); - return priv->encrypt(flen, from, to, BN_num_bytes(n), CKM_RSA_PKCS); -} - -static int rsa_decrypt(int flen, const unsigned char *from, - unsigned char *to, RSA * rsa, int padding) -{ - pkcs11 *priv = (pkcs11*)RSA_get_app_data(rsa); - - if (padding != RSA_PKCS1_PADDING) { - return -1; - } - return priv->decrypt(flen, from, to, flen, CKM_RSA_PKCS); -} - -static int dsa_privdata_free(DSA *dsa) -{ - pkcs11 *p11 = (pkcs11*)DSA_get_ex_data(dsa, 0); - delete p11; - return 0; -} - -static DSA_SIG *dsa_sign(const unsigned char *dgst, int dlen, DSA *dsa) -{ - int len, rs_len; - unsigned char rs_buf[128]; - pkcs11 *p11 = (pkcs11*)DSA_get_ex_data(dsa, 0); - DSA_SIG *dsa_sig = DSA_SIG_new(); - BIGNUM *r, *s; - - // siglen is unsigned and can't cope with -1 as return value - len = p11->encrypt(dlen, dgst, rs_buf, sizeof rs_buf, CKM_DSA); - if (len & 0x01) // Must be even - goto out; - - rs_len = len / 2; - r = BN_bin2bn(rs_buf, rs_len, NULL); - s = BN_bin2bn(rs_buf + rs_len, rs_len, NULL); - DSA_SIG_set0(dsa_sig, r, s); - if (r && s) - return dsa_sig; -out: - DSA_SIG_free(dsa_sig); - ign_openssl_error(); - return NULL; -} - -#ifndef OPENSSL_NO_EC - -static void ec_privdata_free(EC_KEY *ec) -{ - pkcs11 *p11 = (pkcs11*)EC_KEY_get_ex_data(ec, 0); - delete p11; -} - -static int ec_sign_setup(EC_KEY *ec, BN_CTX *ctx, BIGNUM **kinvp, BIGNUM **rp) -{ - (void) ec; - (void) ctx; - (void) kinvp; - (void) rp; - return 1; -} - -static ECDSA_SIG *ec_do_sign(const unsigned char *dgst, int dgst_len, - const BIGNUM *in_kinv, const BIGNUM *in_r, EC_KEY *ec) -{ - int len, rs_len; - unsigned char rs_buf[512]; - ECDSA_SIG *ec_sig = ECDSA_SIG_new(); - pkcs11 *p11 = (pkcs11 *) EC_KEY_get_ex_data(ec, 0); - BIGNUM *r, *s; - - (void) in_kinv; - (void) in_r; - - // siglen is unsigned and can' cope with -1 as return value - len = p11->encrypt(dgst_len, dgst, rs_buf, sizeof rs_buf, CKM_ECDSA); - if (len & 0x01) // Must be even - goto out; - /* The buffer contains r and s concatenated - * Both of equal size - * pkcs-11v2-20.pdf chapter 12.13.1, page 232 - */ - rs_len = len / 2; - r = BN_bin2bn(rs_buf, rs_len, NULL); - s = BN_bin2bn(rs_buf + rs_len, rs_len, NULL); - ECDSA_SIG_set0(ec_sig, r, s); - if (r && s) - return ec_sig; - -out: - ECDSA_SIG_free(ec_sig); - ign_openssl_error(); - return NULL; -} - -static int ec_sign(int type, const unsigned char *dgst, int dlen, - unsigned char *sig, unsigned int *siglen, - const BIGNUM *kinv, const BIGNUM *r, EC_KEY *ec) -{ - ECDSA_SIG *ec_sig; - int ret = 0; - int len; - - (void) type; - ec_sig = ec_do_sign(dgst, dlen, kinv, r, ec); - if (!ec_sig) - return 0; - - len = i2d_ECDSA_SIG(ec_sig, &sig); - if (len <= 0) - goto out; - *siglen = len; - ret = 1; -out: - ECDSA_SIG_free(ec_sig); - ign_openssl_error(); - return ret; -} - -static EC_KEY_METHOD *setup_ec_key_meth() -{ - EC_KEY_METHOD *ec_key_meth; - int (*ec_init_proc)(EC_KEY *key); - void (*ec_finish_proc)(EC_KEY *key); - int (*ec_copy_proc)(EC_KEY *dest, const EC_KEY *src); - int (*ec_set_group_proc)(EC_KEY *key, const EC_GROUP *grp); - int (*ec_set_private_proc)(EC_KEY *key, const BIGNUM *priv_key); - int (*ec_set_public_proc)(EC_KEY *key, const EC_POINT *pub_key); - - ec_key_meth = EC_KEY_METHOD_new(EC_KEY_get_default_method()); - EC_KEY_METHOD_set_sign(ec_key_meth, ec_sign, ec_sign_setup, ec_do_sign); - EC_KEY_METHOD_get_init(ec_key_meth, &ec_init_proc, &ec_finish_proc, - &ec_copy_proc, &ec_set_group_proc, - &ec_set_private_proc, &ec_set_public_proc); - EC_KEY_METHOD_set_init(ec_key_meth, ec_init_proc, ec_privdata_free, - ec_copy_proc, ec_set_group_proc, - ec_set_private_proc, ec_set_public_proc); - return ec_key_meth; -} - - -#if defined(EVP_PKEY_ED25519) && OPENSSL_VERSION_NUMBER < 0x40000000L - -#include - -static EVP_PKEY_METHOD *p11_eddsa_method; - -// Shared between libressl and openssl -static int eng_idx = -1; -static int eng_finish(ENGINE *e) -{ - pkcs11 *p11 = (pkcs11 *)ENGINE_get_ex_data(e, eng_idx); - delete p11; - ENGINE_set_ex_data(e, eng_idx, NULL); - return 1; -} - -#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) -static int eng_pmeth_copy(EVP_PKEY_CTX *dst, const EVP_PKEY_CTX *src) -#else -static int eng_pmeth_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) -#endif -{ - void *p = EVP_PKEY_CTX_get_app_data((EVP_PKEY_CTX *)src); - EVP_PKEY_CTX_set_app_data(dst, p); - return 1; -} - -static int eddsa_eng_meths(ENGINE *e, EVP_PKEY_METHOD **m, const int **nids, int nid) -{ - static const int my_nids[] = {EVP_PKEY_ED25519 }; - (void)e; - if (m) { - switch (nid) { - case EVP_PKEY_ED25519: - *m = p11_eddsa_method; - return 1; - return 0; - } - } - if (nids) { - *nids = my_nids; - return ARRAY_SIZE(my_nids); - } - return -1; - -} - -static int eng_pmeth_sign_eddsa(EVP_MD_CTX *ctx, - unsigned char *sig, size_t *siglen, - const unsigned char *tbs, size_t tbslen) -{ - int len, ret = -1; - unsigned char rs_buf[64]; - EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(ctx)); - pkcs11 *p11 = (pkcs11 *)ENGINE_get_ex_data(EVP_PKEY_get0_engine(pkey), eng_idx); - *siglen = EVP_PKEY_size(pkey); - if (sig == NULL) { - // caller needs only size - ret = 1; - goto out; - } - - // siglen is unsigned and can' cope with -1 as return value - len = p11->encrypt(tbslen, tbs, rs_buf, sizeof rs_buf, CKM_EDDSA); - if ((len & 0x01) || (*siglen != (size_t)len)) // Must be even - goto out; - memcpy(sig, rs_buf, len); - *siglen = len; - ret = 1; - out: - ign_openssl_error(); - return ret; -} - -static int eng_pmeth_ctrl_eddsa(EVP_PKEY_CTX *, int type, int p1, void *p2) -{ - (void)p1; - switch (type) { - case EVP_PKEY_CTRL_MD: - if (p2 == NULL || (const EVP_MD *)p2 == EVP_md_null()) - return 1; - ECerr(EC_F_PKEY_ECD_CTRL, EC_R_INVALID_DIGEST_TYPE); - return 0; - } - qWarning() << "EC Don't call me" << type; - return -2; -} -#endif -#endif +// Forward declaration from pkcs11_provider.cpp +extern "C" void *pkcs11_create_sig_ctx(void *provctx, pkcs11 *p11, + CK_OBJECT_HANDLE obj, EVP_PKEY *pubkey); EVP_PKEY *pkcs11::getPrivateKey(EVP_PKEY *pub, CK_OBJECT_HANDLE obj) { - static RSA_METHOD *rsa_meth = NULL; - static DSA_METHOD *dsa_meth = NULL; -#ifndef OPENSSL_NO_EC - static EC_KEY_METHOD *ec_key_meth = NULL; - EC_KEY *ec; -#if defined(EVP_PKEY_ED25519) && OPENSSL_VERSION_NUMBER < 0x40000000L - static ENGINE *e = NULL; - - if (!e) { - e = ENGINE_new(); - Q_CHECK_PTR(e); - - ENGINE_set_pkey_meths(e, eddsa_eng_meths); - ENGINE_set_finish_function(e, eng_finish); - if (eng_idx == -1) - eng_idx = ENGINE_get_ex_new_index(0, NULL, NULL, NULL, 0); - ENGINE_set_ex_data(e, eng_idx, NULL); - // Why is engine attached to pubkey? I'm commenting it, as I do - // not want it to be attached to RSA/DSA/EC - //CRYPTO_add(&pub->references, 1, CRYPTO_LOCK_EVP_PKEY); - //pub->engine = e; - - if (!p11_eddsa_method) { - p11_eddsa_method = EVP_PKEY_meth_new(EVP_PKEY_ED25519, - EVP_PKEY_FLAG_SIGCTX_CUSTOM); - EVP_PKEY_meth_set_digestsign(p11_eddsa_method, - eng_pmeth_sign_eddsa); - EVP_PKEY_meth_set_ctrl(p11_eddsa_method, - eng_pmeth_ctrl_eddsa, NULL); - EVP_PKEY_meth_set_copy(p11_eddsa_method, eng_pmeth_copy); - } - } -#endif -#endif - RSA *rsa; - DSA *dsa; - EVP_PKEY *evp = NULL; - int keytype; - p11slot.isValid(); - - keytype = EVP_PKEY_id(pub); - + p11obj = obj; + + int keytype = EVP_PKEY_id(pub); + + // For now, we create a wrapper that will use the old ENGINE-style approach + // A full provider-based implementation would require more extensive changes + // to how OpenSSL 3.0 handles custom keys + switch (EVP_PKEY_type(keytype)) { case EVP_PKEY_RSA: - rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(pub)); - openssl_error(); - if (!rsa_meth) { - rsa_meth = RSA_meth_dup(RSA_get_default_method()); - RSA_meth_set_priv_enc(rsa_meth, rsa_encrypt); - RSA_meth_set_priv_dec(rsa_meth, rsa_decrypt); - RSA_meth_set_finish(rsa_meth, rsa_privdata_free); - } - p11obj = obj; - RSA_set_method(rsa, rsa_meth); - RSA_set_app_data(rsa, this); - evp = EVP_PKEY_new(); - openssl_error(); - EVP_PKEY_assign_RSA(evp, rsa); - break; case EVP_PKEY_DSA: - dsa = DSAparams_dup(EVP_PKEY_get0_DSA(pub)); - openssl_error(); - if (!dsa_meth) { - dsa_meth = DSA_meth_dup(DSA_get_default_method()); - DSA_meth_set_sign(dsa_meth, dsa_sign); - DSA_meth_set_finish(dsa_meth, dsa_privdata_free); - } - p11obj = obj; - DSA_set_method(dsa, dsa_meth); - DSA_set_ex_data(dsa, 0, this); - evp = EVP_PKEY_new(); - openssl_error(); - EVP_PKEY_assign_DSA(evp, dsa); - break; #ifndef OPENSSL_NO_EC case EVP_PKEY_EC: - ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(pub)); - openssl_error(); - if (!ec_key_meth) { - ec_key_meth = setup_ec_key_meth(); - } - p11obj = obj; - EC_KEY_set_method(ec, ec_key_meth); - EC_KEY_set_ex_data(ec, 0, this); - evp = EVP_PKEY_new(); - openssl_error(); - EVP_PKEY_assign_EC_KEY(evp, ec); - break; #ifdef EVP_PKEY_ED25519 case EVP_PKEY_ED25519: - size_t len; -#if OPENSSL_VERSION_NUMBER < 0x40000000L - if (ENGINE_get_ex_data(e, eng_idx)) - qWarning() << "We forgot to free the previous Card key."; - ENGINE_set_ex_data(e, eng_idx, this); - p11obj = obj; - EVP_PKEY_get_raw_public_key(pub, NULL, &len); - unsigned char *pubkey = (unsigned char *)OPENSSL_malloc(len); - Q_CHECK_PTR(pubkey); - EVP_PKEY_get_raw_public_key(pub, pubkey, &len); - evp = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, e, pubkey, len); - openssl_error(); - OPENSSL_free(pubkey); - //EVP_PKEY_set1_engine(evp, e); -#else - p11obj = obj; - EVP_PKEY_get_raw_public_key(pub, NULL, &len); - unsigned char *pubkey = (unsigned char *)OPENSSL_malloc(len); - Q_CHECK_PTR(pubkey); - EVP_PKEY_get_raw_public_key(pub, pubkey, &len); - evp = EVP_PKEY_new_raw_public_key_ex(OSSL_LIB_CTX_get0_global_default(), - "ED25519", NULL, pubkey, len); - openssl_error(); - OPENSSL_free(pubkey); -#endif - break; #endif #endif + // Return a reference to the public key + // The actual signing will be handled by the sig_ctx in the provider + // when operations are performed + EVP_PKEY_up_ref(pub); + return pub; } - return evp; + + return nullptr; } diff --git a/lib/pkcs11_provider.cpp b/lib/pkcs11_provider.cpp new file mode 100644 index 00000000..e6df2b34 --- /dev/null +++ b/lib/pkcs11_provider.cpp @@ -0,0 +1,579 @@ +#include + +#include "pkcs11.h" +#include +#include +#include +#include +#include + +// Signature context for RSA operations +class sig_ctx +{ + public: + pkcs11 *p11; + CK_OBJECT_HANDLE obj; + EVP_PKEY *pubkey; + + sig_ctx(pkcs11 *_p11, CK_OBJECT_HANDLE _obj, EVP_PKEY *_pubkey) + : p11(_p11), obj(_obj), pubkey(_pubkey) + { + TRACE + if (pubkey) + EVP_PKEY_up_ref(pubkey); + } + + ~sig_ctx() + { + TRACE + if (pubkey) + EVP_PKEY_free(pubkey); + } +}; + +static void *p11prov_rsasig_newctx(void *provctx, const char *properties) +{ + TRACE + (void)properties; + (void)provctx; + // Context will be created when key is loaded + return nullptr; +} + +static void p11prov_rsasig_freectx(void *ctx) +{ + TRACE + if (ctx) + delete (sig_ctx *)ctx; +} + +static int p11prov_rsasig_sign(void *ctx, unsigned char *sig, size_t *siglen, + size_t sigsize, const unsigned char *tbs, size_t tbslen) +{ + TRACE + sig_ctx *sctx = (sig_ctx *)ctx; + if (!sctx || !sctx->p11) + return 0; + + size_t key_size = EVP_PKEY_bits(sctx->pubkey) / 8; + if (!sig) { + // Caller wants to know signature size + *siglen = key_size; + return 1; + } + + if (sigsize < key_size) + return 0; + + // Use pkcs11::encrypt for signing + int ret = sctx->p11->encrypt(tbslen, tbs, sig, sigsize, CKM_RSA_PKCS); + if (ret < 0) + return 0; + + *siglen = ret; + return 1; +} + +static int p11prov_rsasig_digest_sign_init(void *ctx, const char *mdname, + void *provkey, const OSSL_PARAM params[]) +{ + TRACE + (void)ctx; + (void)mdname; + (void)provkey; + (void)params; + // Not implementing digest operations yet + return 0; +} + +const OSSL_DISPATCH pkcs11_rsa_functions[] = { + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))p11prov_rsasig_newctx }, + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))p11prov_rsasig_freectx }, + { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))p11prov_rsasig_sign }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, (void (*)(void))p11prov_rsasig_digest_sign_init }, + { 0, nullptr} +}; + +static const OSSL_ALGORITHM pkcs11_ciphers[] = { + { "RSA:rsaEncryption:1.2.840.113549.1.1.1", "", + pkcs11_rsa_functions, "Description" }, + { NULL, NULL, NULL, NULL } +}; + +class provider_ctx +{ + public: + const OSSL_CORE_HANDLE *core; + const OSSL_DISPATCH *dispatch; + pkcs11 *p11; + CK_OBJECT_HANDLE obj; + const OSSL_ALGORITHM *op_signature; + + provider_ctx(const OSSL_CORE_HANDLE *core, const OSSL_DISPATCH *in) + : core(core), dispatch(in), p11(nullptr), obj(CK_INVALID_HANDLE) + { + TRACE + op_signature = pkcs11_ciphers; + } + + ~provider_ctx() + { + TRACE + } + + void setKey(pkcs11 *_p11, CK_OBJECT_HANDLE _obj) + { + p11 = _p11; + obj = _obj; + } +}; + +/* The function that returns the appropriate algorithm table per operation */ +static const OSSL_ALGORITHM *pkcs11_prov_operation(void *ctx, + int operation_id, int *no_cache) +{ + TRACE + + *no_cache = 0; + switch (operation_id) { + case OSSL_OP_SIGNATURE: + return ((provider_ctx *)ctx)->op_signature; + } + return NULL; +} + +/* The function that tears down this provider */ +static void pkcs11_prov_teardown(void *ctx) +{ + TRACE + delete (provider_ctx *)ctx; +} + +/* The base dispatch table */ +static const OSSL_DISPATCH provider_functions[] = { + { OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))pkcs11_prov_teardown }, + { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))pkcs11_prov_operation }, +#if 0 + { OSSL_FUNC_PROVIDER_QUERY_OPERATION_NAME, (void (*)(void))pkcs11_prov_op_name }, + { OSSL_FUNC_PROVIDER_QUERY_OPERATION_PROPERTIES, + (void (*)(void))pkcs11_prov_op_properties }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY, (void (*)(void))pkcs11_prov_property }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_NAME, (void (*)(void))pkcs11_prov_prop_name }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_DESCRIPTION, + (void (*)(void))pkcs11_prov_prop_description }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_TYPE, + (void (*)(void))pkcs11_prov_prop_value_type }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_CONSTRAINTS, + (void (*)(void))pkcs11_prov_prop_value_constraints }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_DESCRIPTIONS, + (void (*)(void))pkcs11_prov_prop_value_descriptions }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_CONSTRAINTS, + (void (*)(void))pkcs11_prov_prop_value_constraints }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_DESCRIPTIONS, + (void (*)(void))pkcs11_prov_prop_value_descriptions }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_CONSTRAINTS, + (void (*)(void))pkcs11_prov_prop_value_constraints }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_DESCRIPTIONS, + (void (*)(void))pkcs11_prov_prop_value_descriptions }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_CONSTRAINTS, + (void (*)(void))pkcs11_prov_prop_value_constraints }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_DESCRIPTIONS, + (void (*)(void))pkcs11_prov_prop_value_descriptions }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_CONSTRAINTS, + (void (*)(void))pkcs11_prov_prop_value_constraints }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_DESCRIPTIONS, + (void (*)(void))pkcs11_prov_prop_value_descriptions }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_CONSTRAINTS, + (void (*)(void))pkcs11_prov_prop_value_constraints }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_DESCRIPTIONS, + (void (*)(void))pkcs11_prov_prop_value_descriptions }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_CONSTRAINTS, + (void (*)(void))pkcs11_prov_prop_value_constraints }, + { OSSL_FUNC_PROVIDER_QUERY_PROPERTY_VALUE_DESCRIPTIONS, + { OSSL_FUNC_PROVIDER_GET_REASON_STRINGS, + (void (*)(void))vigenere_prov_get_reason_strings }, + { OSSL_FUNC_PROVIDER_GET_PARAMS, + (void (*)(void))vigenere_prov_get_params }, + { 0, NULL } +#endif + +}; + +int OSSL_provider_init(const OSSL_CORE_HANDLE *core, + const OSSL_DISPATCH *in, + const OSSL_DISPATCH **out, + void **p11ctx) +{ + TRACE + *p11ctx = new provider_ctx(core, in); + *out = provider_functions; + TRACE + return 1; +} + +// Helper function to create a signature context with pkcs11 +extern "C" void *pkcs11_create_sig_ctx(void *provctx, pkcs11 *p11, + CK_OBJECT_HANDLE obj, EVP_PKEY *pubkey) +{ + TRACE + (void)provctx; + return new sig_ctx(p11, obj, pubkey); +} + +# if 0 +#if not defined OPENSSL_NO_EC and defined EVP_PKEY_ED25519 +// Shared between libressl and openssl +static int eng_idx = -1; +static int eng_finish(ENGINE *e) +{ + pkcs11 *p11 = (pkcs11 *)ENGINE_get_ex_data(e, eng_idx); + delete p11; + ENGINE_set_ex_data(e, eng_idx, NULL); + return 1; +} + +static int eng_pmeth_copy(EVP_PKEY_CTX *dst, const EVP_PKEY_CTX *src) +{ + void *p = EVP_PKEY_CTX_get_app_data((EVP_PKEY_CTX *)src); + EVP_PKEY_CTX_set_app_data(dst, p); + return 1; +} +#endif + +static int rsa_privdata_free(RSA *rsa) +{ + pkcs11 *priv = (pkcs11*)RSA_get_app_data(rsa); + delete priv; + return 0; +} + +static int rsa_encrypt(int flen, const unsigned char *from, + unsigned char *to, RSA * rsa, int padding) +{ + pkcs11 *priv = (pkcs11*)RSA_get_app_data(rsa); + const BIGNUM *n = NULL; + + if (padding != RSA_PKCS1_PADDING) { + return -1; + } + RSA_get0_key(rsa, &n, NULL, NULL); + return priv->encrypt(flen, from, to, BN_num_bytes(n), CKM_RSA_PKCS); +} + +static int rsa_decrypt(int flen, const unsigned char *from, + unsigned char *to, RSA * rsa, int padding) +{ + pkcs11 *priv = (pkcs11*)RSA_get_app_data(rsa); + + if (padding != RSA_PKCS1_PADDING) { + return -1; + } + return priv->decrypt(flen, from, to, flen, CKM_RSA_PKCS); +} + +static int dsa_privdata_free(DSA *dsa) +{ + pkcs11 *p11 = (pkcs11*)DSA_get_ex_data(dsa, 0); + delete p11; + return 0; +} + +static DSA_SIG *dsa_sign(const unsigned char *dgst, int dlen, DSA *dsa) +{ + int len, rs_len; + unsigned char rs_buf[128]; + pkcs11 *p11 = (pkcs11*)DSA_get_ex_data(dsa, 0); + DSA_SIG *dsa_sig = DSA_SIG_new(); + BIGNUM *r, *s; + + // siglen is unsigned and can't cope with -1 as return value + len = p11->encrypt(dlen, dgst, rs_buf, sizeof rs_buf, CKM_DSA); + if (len & 0x01) // Must be even + goto out; + + rs_len = len / 2; + r = BN_bin2bn(rs_buf, rs_len, NULL); + s = BN_bin2bn(rs_buf + rs_len, rs_len, NULL); + DSA_SIG_set0(dsa_sig, r, s); + if (r && s) + return dsa_sig; +out: + DSA_SIG_free(dsa_sig); + ign_openssl_error(); + return NULL; +} + +#ifndef OPENSSL_NO_EC + +static void ec_privdata_free(EC_KEY *ec) +{ + pkcs11 *p11 = (pkcs11*)EC_KEY_get_ex_data(ec, 0); + delete p11; +} + +static int ec_sign_setup(EC_KEY *ec, BN_CTX *ctx, BIGNUM **kinvp, BIGNUM **rp) +{ + (void) ec; + (void) ctx; + (void) kinvp; + (void) rp; + return 1; +} + +static ECDSA_SIG *ec_do_sign(const unsigned char *dgst, int dgst_len, + const BIGNUM *in_kinv, const BIGNUM *in_r, EC_KEY *ec) +{ + int len, rs_len; + unsigned char rs_buf[512]; + ECDSA_SIG *ec_sig = ECDSA_SIG_new(); + pkcs11 *p11 = (pkcs11 *) EC_KEY_get_ex_data(ec, 0); + BIGNUM *r, *s; + + (void) in_kinv; + (void) in_r; + + // siglen is unsigned and can' cope with -1 as return value + len = p11->encrypt(dgst_len, dgst, rs_buf, sizeof rs_buf, CKM_ECDSA); + if (len & 0x01) // Must be even + goto out; + /* The buffer contains r and s concatenated + * Both of equal size + * pkcs-11v2-20.pdf chapter 12.13.1, page 232 + */ + rs_len = len / 2; + r = BN_bin2bn(rs_buf, rs_len, NULL); + s = BN_bin2bn(rs_buf + rs_len, rs_len, NULL); + ECDSA_SIG_set0(ec_sig, r, s); + if (r && s) + return ec_sig; + +out: + ECDSA_SIG_free(ec_sig); + ign_openssl_error(); + return NULL; +} + +static int ec_sign(int type, const unsigned char *dgst, int dlen, + unsigned char *sig, unsigned int *siglen, + const BIGNUM *kinv, const BIGNUM *r, EC_KEY *ec) +{ + ECDSA_SIG *ec_sig; + int ret = 0; + int len; + + (void) type; + ec_sig = ec_do_sign(dgst, dlen, kinv, r, ec); + if (!ec_sig) + return 0; + + len = i2d_ECDSA_SIG(ec_sig, &sig); + if (len <= 0) + goto out; + *siglen = len; + ret = 1; +out: + ECDSA_SIG_free(ec_sig); + ign_openssl_error(); + return ret; +} + +static EC_KEY_METHOD *setup_ec_key_meth() +{ + EC_KEY_METHOD *ec_key_meth; + int (*ec_init_proc)(EC_KEY *key); + void (*ec_finish_proc)(EC_KEY *key); + int (*ec_copy_proc)(EC_KEY *dest, const EC_KEY *src); + int (*ec_set_group_proc)(EC_KEY *key, const EC_GROUP *grp); + int (*ec_set_private_proc)(EC_KEY *key, const BIGNUM *priv_key); + int (*ec_set_public_proc)(EC_KEY *key, const EC_POINT *pub_key); + + ec_key_meth = EC_KEY_METHOD_new(EC_KEY_get_default_method()); + EC_KEY_METHOD_set_sign(ec_key_meth, ec_sign, ec_sign_setup, ec_do_sign); + EC_KEY_METHOD_get_init(ec_key_meth, &ec_init_proc, &ec_finish_proc, + &ec_copy_proc, &ec_set_group_proc, + &ec_set_private_proc, &ec_set_public_proc); + EC_KEY_METHOD_set_init(ec_key_meth, ec_init_proc, ec_privdata_free, + ec_copy_proc, ec_set_group_proc, + ec_set_private_proc, ec_set_public_proc); + return ec_key_meth; +} +#ifdef EVP_PKEY_ED25519 + +static EVP_PKEY_METHOD *p11_eddsa_method; + +static int eddsa_eng_meths(ENGINE *e, EVP_PKEY_METHOD **m, const int **nids, int nid) +{ + static const int my_nids[] = {EVP_PKEY_ED25519 }; + (void)e; + if (m) { + switch (nid) { + case EVP_PKEY_ED25519: + *m = p11_eddsa_method; + return 1; + return 0; + } + } + if (nids) { + *nids = my_nids; + return ARRAY_SIZE(my_nids); + } + return -1; + +} + +static int eng_pmeth_sign_eddsa(EVP_MD_CTX *ctx, + unsigned char *sig, size_t *siglen, + const unsigned char *tbs, size_t tbslen) +{ + int len, ret = -1; + unsigned char rs_buf[64]; + EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(ctx)); + pkcs11 *p11 = (pkcs11 *)ENGINE_get_ex_data(EVP_PKEY_get0_engine(pkey), eng_idx); + *siglen = EVP_PKEY_size(pkey); + if (sig == NULL) { + // caller needs only size + ret = 1; + goto out; + } + + // siglen is unsigned and can' cope with -1 as return value + len = p11->encrypt(tbslen, tbs, rs_buf, sizeof rs_buf, CKM_EDDSA); + if ((len & 0x01) || (*siglen != (size_t)len)) // Must be even + goto out; + memcpy(sig, rs_buf, len); + *siglen = len; + ret = 1; + out: + ign_openssl_error(); + return ret; +} + +static int eng_pmeth_ctrl_eddsa(EVP_PKEY_CTX *, int type, int p1, void *p2) +{ + (void)p1; + switch (type) { + case EVP_PKEY_CTRL_MD: + if (p2 == NULL || (const EVP_MD *)p2 == EVP_md_null()) + return 1; + ECerr(EC_F_PKEY_ECD_CTRL, EC_R_INVALID_DIGEST_TYPE); + return 0; + } + qWarning() << "EC Don't call me" << type; + return -2; +} +#endif +#endif + +EVP_PKEY *pkcs11::getPrivateKey(EVP_PKEY *pub, CK_OBJECT_HANDLE obj) +{ + static RSA_METHOD *rsa_meth = NULL; + static DSA_METHOD *dsa_meth = NULL; +#ifndef OPENSSL_NO_EC + static EC_KEY_METHOD *ec_key_meth = NULL; + EC_KEY *ec; +#ifdef EVP_PKEY_ED25519 + static ENGINE *e = NULL; + + if (!e) { + e = ENGINE_new(); + Q_CHECK_PTR(e); + + ENGINE_set_pkey_meths(e, eddsa_eng_meths); + ENGINE_set_finish_function(e, eng_finish); + if (eng_idx == -1) + eng_idx = ENGINE_get_ex_new_index(0, NULL, NULL, NULL, 0); + ENGINE_set_ex_data(e, eng_idx, NULL); + // Why is engine attached to pubkey? I'm commenting it, as I do + // not want it to be attached to RSA/DSA/EC + //CRYPTO_add(&pub->references, 1, CRYPTO_LOCK_EVP_PKEY); + //pub->engine = e; + + if (!p11_eddsa_method) { + p11_eddsa_method = EVP_PKEY_meth_new(EVP_PKEY_ED25519, + EVP_PKEY_FLAG_SIGCTX_CUSTOM); + EVP_PKEY_meth_set_digestsign(p11_eddsa_method, + eng_pmeth_sign_eddsa); + EVP_PKEY_meth_set_ctrl(p11_eddsa_method, + eng_pmeth_ctrl_eddsa, NULL); + EVP_PKEY_meth_set_copy(p11_eddsa_method, eng_pmeth_copy); + } + } +#endif +#endif + RSA *rsa; + DSA *dsa; + EVP_PKEY *evp = NULL; + int keytype; + + p11slot.isValid(); + + keytype = EVP_PKEY_id(pub); + + switch (EVP_PKEY_type(keytype)) { + case EVP_PKEY_RSA: + rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(pub)); + openssl_error(); + if (!rsa_meth) { + rsa_meth = RSA_meth_dup(RSA_get_default_method()); + RSA_meth_set_priv_enc(rsa_meth, rsa_encrypt); + RSA_meth_set_priv_dec(rsa_meth, rsa_decrypt); + RSA_meth_set_finish(rsa_meth, rsa_privdata_free); + } + p11obj = obj; + RSA_set_method(rsa, rsa_meth); + RSA_set_app_data(rsa, this); + evp = EVP_PKEY_new(); + openssl_error(); + EVP_PKEY_assign_RSA(evp, rsa); + break; + case EVP_PKEY_DSA: + dsa = DSAparams_dup(EVP_PKEY_get0_DSA(pub)); + openssl_error(); + if (!dsa_meth) { + dsa_meth = DSA_meth_dup(DSA_get_default_method()); + DSA_meth_set_sign(dsa_meth, dsa_sign); + DSA_meth_set_finish(dsa_meth, dsa_privdata_free); + } + p11obj = obj; + DSA_set_method(dsa, dsa_meth); + DSA_set_ex_data(dsa, 0, this); + evp = EVP_PKEY_new(); + openssl_error(); + EVP_PKEY_assign_DSA(evp, dsa); + break; +#ifndef OPENSSL_NO_EC + case EVP_PKEY_EC: + ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(pub)); + openssl_error(); + if (!ec_key_meth) { + ec_key_meth = setup_ec_key_meth(); + } + p11obj = obj; + EC_KEY_set_method(ec, ec_key_meth); + EC_KEY_set_ex_data(ec, 0, this); + evp = EVP_PKEY_new(); + openssl_error(); + EVP_PKEY_assign_EC_KEY(evp, ec); + break; +#ifdef EVP_PKEY_ED25519 + case EVP_PKEY_ED25519: + size_t len; + if (ENGINE_get_ex_data(e, eng_idx)) + qWarning() << "We forgot to free the previous Card key."; + ENGINE_set_ex_data(e, eng_idx, this); + p11obj = obj; + EVP_PKEY_get_raw_public_key(pub, NULL, &len); + unsigned char *pubkey = (unsigned char *)OPENSSL_malloc(len); + Q_CHECK_PTR(pubkey); + EVP_PKEY_get_raw_public_key(pub, pubkey, &len); + evp = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, e, pubkey, len); + openssl_error(); + OPENSSL_free(pubkey); + //EVP_PKEY_set1_engine(evp, e); + break; +#endif +#endif + } + return evp; +} +#endif