From 589f8d748b07d2c5c744ade8b651ffb6afae9098 Mon Sep 17 00:00:00 2001 From: Constantine Grantcharov Date: Sun, 24 Apr 2016 02:36:28 -0400 Subject: [PATCH 1/3] Enable ECDHE temporary parameters in ASIO SSL Implemented API context::use_tmp_ecdh(std::string& certificate) to enable SSL_CTX_set_tmp_ecdh() from OpenSSL. This functionality was missing and certificates using ECC were not able to use ECDHE due to missing temporary ECDH parameters. Using this new API, the user can just pass in their certificate and the API will identify the ECC curve and use it's generator point to create new temporary key pairs to provide Perfect Forward Secrecy (PFS). --- asio/include/asio/ssl/context.hpp | 36 +++++++++++++ asio/include/asio/ssl/impl/context.ipp | 70 +++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/asio/include/asio/ssl/context.hpp b/asio/include/asio/ssl/context.hpp index 9657d774b7..a644796add 100644 --- a/asio/include/asio/ssl/context.hpp +++ b/asio/include/asio/ssl/context.hpp @@ -667,6 +667,37 @@ class context ASIO_DECL ASIO_SYNC_OP_VOID use_tmp_dh_file( const std::string& filename, asio::error_code& ec); + /// Use the specified certificate to obtain temporary Diffie-Hellman parameters + /// for Elliptic Curve Diffie-Hellman. + /** + * This function is used to load Elliptic Curve Diffie-Hellman parameters into + * the context from a certificate based on Elliptic Curve Cryptography (ECC). + * + * @param certificate The name of the file containing the ECC-based certificate. + * The file must use the PEM format. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_set_tmp_ecdh. + */ + ASIO_DECL void use_tmp_ecdh(const std::string& certificate); + + /// Use the specified certificate to obtain temporary Diffie-Hellman parameters + /// for Elliptic Curve Diffie-Hellman. + /** + * This function is used to load Elliptic Curve Diffie-Hellman parameters into + * the context from a certificate based on Elliptic Curve Cryptography (ECC). + * + * @param certificate The name of the file containing the ECC-based certificate. + * The file must use the PEM format. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note Calls @c SSL_CTX_set_tmp_ecdh. + */ + ASIO_DECL ASIO_SYNC_OP_VOID use_tmp_ecdh( + const std::string& certificate, asio::error_code& ec); + /// Set the password callback. /** * This function is used to specify a callback function to obtain password @@ -714,6 +745,7 @@ class context struct evp_pkey_cleanup; struct rsa_cleanup; struct dh_cleanup; + struct ec_key_cleanup; // Helper function used to set a peer certificate verification callback. ASIO_DECL ASIO_SYNC_OP_VOID do_set_verify_callback( @@ -735,6 +767,10 @@ class context ASIO_DECL ASIO_SYNC_OP_VOID do_use_tmp_dh( BIO* bio, asio::error_code& ec); + // Helper function to set the temprorary ECC Diffie-Hellman parameters from a BIO. + ASIO_DECL ASIO_SYNC_OP_VOID do_use_tmp_ecdh( + BIO* bio, asio::error_code& ec); + // Helper function to make a BIO from a memory buffer. ASIO_DECL BIO* make_buffer_bio(const const_buffer& b); diff --git a/asio/include/asio/ssl/impl/context.ipp b/asio/include/asio/ssl/impl/context.ipp index 7a19f045b7..a40e35ebfd 100644 --- a/asio/include/asio/ssl/impl/context.ipp +++ b/asio/include/asio/ssl/impl/context.ipp @@ -59,6 +59,12 @@ struct context::dh_cleanup ~dh_cleanup() { if (p) ::DH_free(p); } }; +struct context::ec_key_cleanup +{ + EC_KEY *p; + ~ec_key_cleanup() { if (p) ::EC_KEY_free(p); } +}; + context::context(context::method m) : handle_(0) { @@ -1096,7 +1102,69 @@ ASIO_SYNC_OP_VOID context::do_use_tmp_dh( ASIO_SYNC_OP_VOID_RETURN(ec); } -ASIO_SYNC_OP_VOID context::do_set_verify_callback( +void context::use_tmp_ecdh(const std::string& certificate) +{ + asio::error_code ec; + use_tmp_ecdh(certificate, ec); + asio::detail::throw_error(ec, "use_tmp_ecdh"); +} + +ASIO_SYNC_OP_VOID context::use_tmp_ecdh(const std::string& certificate, + asio::error_code& ec) +{ + ::ERR_clear_error(); + + bio_cleanup bio = { ::BIO_new_file(certificate.c_str(), "r") }; + if (bio.p) + { + return do_use_tmp_ecdh(bio.p,ec); + } + + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + return ec; +} + +ASIO_SYNC_OP_VOID context::do_use_tmp_ecdh( + BIO* bio, asio::error_code& ec) +{ + ::ERR_clear_error(); + + int nid = NID_undef; + + x509_cleanup x509 = { ::PEM_read_bio_X509(bio, NULL, 0, NULL) }; + if (x509.p) + { + evp_pkey_cleanup pkey = { ::X509_get_pubkey(x509.p) }; + if(pkey.p) + { + int type = EVP_PKEY_type(pkey.p->type); + if(type == EVP_PKEY_EC) + { + const EC_GROUP *group = EC_KEY_get0_group(pkey.p->pkey.ec); + nid = EC_GROUP_get_curve_name(group); + } + } + } + + ec_key_cleanup ec_key = { ::EC_KEY_new_by_curve_name(nid) }; + if(ec_key.p) + { + if (::SSL_CTX_set_tmp_ecdh(handle_, ec_key.p) == 1 ) + { + ec = asio::error_code(); + return ec; + } + } + + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + return ec; +} + +asio::error_code context::do_set_verify_callback( detail::verify_callback_base* callback, asio::error_code& ec) { if (SSL_CTX_get_app_data(handle_)) From 9a902b73c9d6d0aa7377afb0eb3d7493ece6daab Mon Sep 17 00:00:00 2001 From: Constantine Grantcharov Date: Sat, 27 Apr 2019 22:26:52 -0400 Subject: [PATCH 2/3] Rename functions use_tmp_ecdh -> use_tmp_ecdh_file --- asio/include/asio/ssl/context.hpp | 6 +++--- asio/include/asio/ssl/impl/context.ipp | 17 +++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/asio/include/asio/ssl/context.hpp b/asio/include/asio/ssl/context.hpp index a644796add..bb1b922ce5 100644 --- a/asio/include/asio/ssl/context.hpp +++ b/asio/include/asio/ssl/context.hpp @@ -680,7 +680,7 @@ class context * * @note Calls @c SSL_CTX_set_tmp_ecdh. */ - ASIO_DECL void use_tmp_ecdh(const std::string& certificate); + ASIO_DECL void use_tmp_ecdh_file(const std::string& certificate); /// Use the specified certificate to obtain temporary Diffie-Hellman parameters /// for Elliptic Curve Diffie-Hellman. @@ -695,7 +695,7 @@ class context * * @note Calls @c SSL_CTX_set_tmp_ecdh. */ - ASIO_DECL ASIO_SYNC_OP_VOID use_tmp_ecdh( + ASIO_DECL ASIO_SYNC_OP_VOID use_tmp_ecdh_file( const std::string& certificate, asio::error_code& ec); /// Set the password callback. @@ -767,7 +767,7 @@ class context ASIO_DECL ASIO_SYNC_OP_VOID do_use_tmp_dh( BIO* bio, asio::error_code& ec); - // Helper function to set the temprorary ECC Diffie-Hellman parameters from a BIO. + // Helper function to set the temporary ECC Diffie-Hellman parameters from a BIO. ASIO_DECL ASIO_SYNC_OP_VOID do_use_tmp_ecdh( BIO* bio, asio::error_code& ec); diff --git a/asio/include/asio/ssl/impl/context.ipp b/asio/include/asio/ssl/impl/context.ipp index a40e35ebfd..a0a913b41d 100644 --- a/asio/include/asio/ssl/impl/context.ipp +++ b/asio/include/asio/ssl/impl/context.ipp @@ -1102,14 +1102,14 @@ ASIO_SYNC_OP_VOID context::do_use_tmp_dh( ASIO_SYNC_OP_VOID_RETURN(ec); } -void context::use_tmp_ecdh(const std::string& certificate) +void context::use_tmp_ecdh_file(const std::string& certificate) { asio::error_code ec; - use_tmp_ecdh(certificate, ec); - asio::detail::throw_error(ec, "use_tmp_ecdh"); + use_tmp_ecdh_file(certificate, ec); + asio::detail::throw_error(ec, "use_tmp_ecdh_file"); } -ASIO_SYNC_OP_VOID context::use_tmp_ecdh(const std::string& certificate, +ASIO_SYNC_OP_VOID context::use_tmp_ecdh_file(const std::string& certificate, asio::error_code& ec) { ::ERR_clear_error(); @@ -1123,7 +1123,7 @@ ASIO_SYNC_OP_VOID context::use_tmp_ecdh(const std::string& certificate, ec = asio::error_code( static_cast(::ERR_get_error()), asio::error::get_ssl_category()); - return ec; + ASIO_SYNC_OP_VOID_RETURN(ec); } ASIO_SYNC_OP_VOID context::do_use_tmp_ecdh( @@ -1154,17 +1154,18 @@ ASIO_SYNC_OP_VOID context::do_use_tmp_ecdh( if (::SSL_CTX_set_tmp_ecdh(handle_, ec_key.p) == 1 ) { ec = asio::error_code(); - return ec; + ASIO_SYNC_OP_VOID_RETURN(ec); } } ec = asio::error_code( static_cast(::ERR_get_error()), asio::error::get_ssl_category()); - return ec; + ASIO_SYNC_OP_VOID_RETURN(ec); } -asio::error_code context::do_set_verify_callback( + +ASIO_SYNC_OP_VOID context::do_set_verify_callback( detail::verify_callback_base* callback, asio::error_code& ec) { if (SSL_CTX_get_app_data(handle_)) From e47f648a74f2b4da8bf45acc54bda5b2f794db56 Mon Sep 17 00:00:00 2001 From: Constantine Grantcharov Date: Tue, 30 Apr 2019 19:11:38 -0400 Subject: [PATCH 3/3] OpenSSL v1.0 and v1.1 Support - modified to make the code compile with against v1.0 and v1.1 of OpenSSL --- asio/include/asio/ssl/impl/context.ipp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/asio/include/asio/ssl/impl/context.ipp b/asio/include/asio/ssl/impl/context.ipp index a0a913b41d..d025ab6f6d 100644 --- a/asio/include/asio/ssl/impl/context.ipp +++ b/asio/include/asio/ssl/impl/context.ipp @@ -1139,10 +1139,10 @@ ASIO_SYNC_OP_VOID context::do_use_tmp_ecdh( evp_pkey_cleanup pkey = { ::X509_get_pubkey(x509.p) }; if(pkey.p) { - int type = EVP_PKEY_type(pkey.p->type); - if(type == EVP_PKEY_EC) + ec_key_cleanup tmp = { ::EVP_PKEY_get1_EC_KEY(pkey.p) }; + if(tmp.p) { - const EC_GROUP *group = EC_KEY_get0_group(pkey.p->pkey.ec); + const EC_GROUP *group = EC_KEY_get0_group(tmp.p); nid = EC_GROUP_get_curve_name(group); } }