I think there is confusion about this topic because there are a few things that need to be clarified. Let's start by stating that ::asio::ssl::context == SSL_CTX . These two are one.
Secondly, when using boost::asio::ssl , if you aren’t doing something crazy, when you bypass the internal init objects, you don’t need to manually set up crypto callbacks. This is done for you, as you can see in the sources here .
In fact, you can cause problems by doing this because the destructor for the init object works under the assumption that they did this work internally. Take the last bit with salt, because I did not examine it in detail.
Thirdly, I believe that you mix SSL streams with SSL contexts. For simplicity, think of SSL flow as a socket, think of the SSL context as a separate entity that sockets can use for various SSL functions, such as confirming communication with a specific negotiation key or as a server providing information about your server certificate to a connected client so that you could come to terms with the client.
Mentioning threads comes down to preventing possible simultaneous I / O with respect to one particular stream (socket), rather than contexts. Obviously, the problem of reading to the buffer and writing from the same buffer to the same socket will be a problem. Thus, when you deliver chain-wrapped completion handlers to the various ::asio::async_X , you execute a specific order to prevent the above scenario. You can read more in this answer provided by someone who knows a lot more about this than me.
Now, as far as the contexts go, David Schwartz points out the comments and another answer that he wrote, I need to dig out that the whole purpose of the contexts is to provide information that facilitates the SSL function through several SSL streams. It seems to imply that they should essentially be thread safe, given their intended purpose. I believe maybe he speaks in the context of ::asio::ssl::context , only because ::asio::ssl uses thread safety callbacks correctly, or maybe he just speaks in the context of using openSSL correctly in a multithreaded program.
Regardless of this, in addition to such comments and answers to SO and my own practical experience, it is incredibly difficult to find concrete evidence of this in the documentation or clearly define the boundaries between what is and is not thread safe. boost::asio::ssl:context , as David points out, is just a very thin wrapper around SSL_CTX . I would also like to add that this meant giving a more “C ++ ish” feel of working with a basic structure (s). It was probably also designed with some intention of decoupling ::asio::ssl and the base implementation library, but this is not achieved, the two are closely related. David mentions again that this thin wrapper is poorly documented, and you need to look at the implementation to get an idea.
If you are starting to delve into the implementation, there is a fairly simple way to find out what is and is not thread safe when it comes to contexts. You can search for CRYPTO_LOCK_SSL_CTX in sources such as ssl_lib.c .
int SSL_CTX_set_generate_session_id(SSL_CTX *ctx, GEN_SESSION_CB cb) { CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); ctx->generate_session_id = cb; CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); return 1; }
As you can see, CRYPTO_w_lock used, which returns us to the official page about openSSL and threads, here , which says:
OpenSSL can be safely used in multi-threaded applications with at least two callback functions installed, lock_function and threadid_func .
Now we get the full circle in the related source code asio/ssl/detail/impl/openssl_init.ipp in the first paragraph of this answer, where we see:
do_init() { ::SSL_library_init(); ::SSL_load_error_strings(); ::OpenSSL_add_all_algorithms(); mutexes_.resize(::CRYPTO_num_locks()); for (size_t i = 0; i < mutexes_.size(); ++i) mutexes_[i].reset(new boost::asio::detail::mutex); ::CRYPTO_set_locking_callback(&do_init::openssl_locking_func); ::CRYPTO_set_id_callback(&do_init::openssl_id_func);
Note:
CRYPTO_set_ locking_callback
CRYPTO_set_ id_callback
So, at least in terms of ::asio::ssl::context , thread safety here has nothing to do with threads, and everything related to openSSL working like openSSL is designed to work when used correctly in a multi-threaded program.
Returning to the first question, now all this is explained, David also gave the answer very simply in the comments:
The most common method is to use one line for each SSL connection.
Take the example of an HTTPS server that serves the contents of example.com . The server has one context configured with information such as a certificate for example.com . The client connects, this context is used for all connected clients to perform handshakes, etc. You transfer the connected client to a new session object where you are processing this client. It is in this session that you will have one direct, implicit or explicit, to protect the socket , not the context.
While I am by no means an expert, and I welcome corrections to this answer, I put everything I know about these subjects into practice in an open-source transparent HTTPS filter. This is a little more than 50% of comments on the code coefficient with more than 17 thousand lines, so everything that I know is written there (whether it is right or wrong)). If you want to see an example of this material in action, you can look at the source TlsCapableHttpBridge.hpp , which acts as a client on the server for each host, for each connection.
Server contexts and certificates are substituted and generated once and are shared for all clients that pan multiple streams. The only manual locking is during storage and context searching. There is one line on the bridge side : one for the real client socket and one for the upstream connection to the server, although they are not even technically necessary because the order of operations creates an implicit chain anyway.
Please note that the project is under development, as I am rewriting a lot of things (assembly instructions have not yet been presented), but everything is functional in terms of SSL MITM code, so you look at a fully functional class and related components.