diff --git a/asio/include/asio/ssl/context.hpp b/asio/include/asio/ssl/context.hpp index 9657d774b7..bb1b922ce5 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_file(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_file( + 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 temporary 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..d025ab6f6d 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,6 +1102,69 @@ ASIO_SYNC_OP_VOID context::do_use_tmp_dh( ASIO_SYNC_OP_VOID_RETURN(ec); } +void context::use_tmp_ecdh_file(const std::string& certificate) +{ + asio::error_code ec; + use_tmp_ecdh_file(certificate, ec); + asio::detail::throw_error(ec, "use_tmp_ecdh_file"); +} + +ASIO_SYNC_OP_VOID context::use_tmp_ecdh_file(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()); + ASIO_SYNC_OP_VOID_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) + { + ec_key_cleanup tmp = { ::EVP_PKEY_get1_EC_KEY(pkey.p) }; + if(tmp.p) + { + const EC_GROUP *group = EC_KEY_get0_group(tmp.p); + 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(); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + } + + ec = asio::error_code( + static_cast(::ERR_get_error()), + asio::error::get_ssl_category()); + ASIO_SYNC_OP_VOID_RETURN(ec); +} + + ASIO_SYNC_OP_VOID context::do_set_verify_callback( detail::verify_callback_base* callback, asio::error_code& ec) {