In the curl project we currently support eleven different TLS libraries. That is 8 libraries and the OpenSSL “trinity” consisting of BoringSSL, libressl and of course OpenSSL itself.
You could easily be mislead into believing that supporting three libraries that all have a common base would be really easy since they have the same API. But no, it isn’t. Sure, they have the same foundation and they all three have more in common that they differ but still, they all diverge in their own little ways and from my stand-point libressl seems to be the one that causes us the least friction going forward.
Let me also stress that I’m but a user of these projects, I don’t participate in their work and I don’t have any insights into their internal doings or greater goals.
Easy-peacy, very similar to OpenSSL. The biggest obstacle might be that the version numbering is different so an old program that might be adjusted to different OpenSSL features based on version numbers (like curl was) needs some adjusting. There’s a convenient LIBRESSL_VERSION_NUMBER define to detect libressl with.
I regularly build curl against OpenSSL from their git master to get an early head-start when they change things and break backwards compatibility. They’ve increased that behavior since Heartbleed and while I generally agree with their ambitions on making more structs opaque instead of exposing all internals, it also hurts us over and over again when they remove things we’ve been using for years. What’s “funny” is that in almost all cases, their response is “well use this way instead” and it has turned out that there’s an equally old API that is still there that we can use instead. It also tells something about their documentation situation when that is such a common pattern. It’s never been possible to grasp this from just reading docs.
BoringSSL has made great inroads in the market and is used on Android now and more. They don’t do releases(!) and have no version numbers so the only thing we can do is to build from git and there’s no install target in the makefile. There’s no docs for it, they remove APIs from OpenSSL (curl can’t support NTLM nor OCSP stapling when built with it), they’ve changed several data types in the API making it really hard to build curl without warnings. Funnily, they also introduced non-namespaced typedefs prefixed with X509_* that collide with other common headers.
How it can play out in real life
A while ago we noticed BoringSSL had removed the DES_set_odd_parity function which we use in curl. We changed the configure script to look for it and changed the code to survive without it. The lack of that function then also signaled that it wasn’t OpenSSL, it was BoringSSL
BoringSSL moved around things that caused our configure script to no longer detect it as “OpenSSL compliant” because CRYPTO_lock could no longer be found by configure. We changed it to instead search for HMAC_Init and we were fine again.
Time passed and BoringSSL brought back DES_set_odd_parity, so our configure script no longer saw it as BoringSSL (the Android fixed this problem in their git but never sent as the fix). We changed the configure script accordingly to properly use OPENSSL_IS_BORINGSSL instead to detect BoringSSL which was the correct thing anyway and now as a bonus it can thus detect and work with both new and old BoringSSL versions.
A short time after, I again try to build curl against the OpenSSL master branch only to realize they’ve deprecated HMAC_Init that we just recently switched to for detection (since the configure script needs to check for a particular named function within a library to really know that it has detected and can use said library). Sigh, we switched “detect function” again to HMAC_Update. Hopefully this exists in all three and will stick around for a while…
Right now I think we can detect and use all three. It is only a matter of time until one of them will ruin that and we will adapt again.
2 thoughts on “The TLS trinity dance”
What kind of thing can we put in OpenSSL to make it easy for you to detect that this is the one true version and nothing else? A special #define, with a comment indicating that nobody else should include it?
We don’t really have a need to detect “the one true version”. In the curl project we support building with OpenSSL 0.9.7 or later (meaning a very large amount of different versions back to very old ones). We already need lots of configure magic and header #ifdefs just to figure out what the legitimate OpenSSL versions through the history can and cannot do – and adapt code accordingly. Another library with the same API could in theory just go through that the same way. In reality of course we’ve been forced to add more checks for the new libs.
We already detect OpenSSL fine, and we then go on and individually detect libressl and BoringSSL so if we don’t detect one of the two latter ones we assume it is truly a version of “the real” OpenSSL. Actually, it would be great to know what the OpenSSL project thinks is the best way to detect OpenSSL. Like a m4 function for autoconf and a macro for cmake to use to detect it with.
What I’ve missed primarily in OpenSSL, is better docs in general (we’ve had several bugs through the years because nobody could figure out how the APIs were really supposed to be used and what return codes meant etc) and especially docs on how to transition away from the things you deprecate.
Comments are closed.