Open SSL

Upgrade To OpenSSL 1.1.0

February 14, 2017 by Rich Salz

I recently blogged about what Akamai is doing to help OpenSSL deliver TLS 1.3 to most of the Internet, and why TLS 1.3 is important and useful in making the internet faster and more secure. It ended with the suggestion that application owners start porting to OpenSSL version 1.1.0 now. This post will offer some reasons why, and advice on how to do so.

The first reason is that it will make it easier and quicker for your applications to start using TLS 1.3. OpenSSL has committed to making the next release 1.1.1, and according to the project policy that carries some guarantees.

Now of course, OpenSSL is an open source project, and the word "guarantee" is a little strong, but the team tries very hard to follow the policy they updated in their release strategy. That policy says that the next release will be binary compatible with 1.1.0, and that "all" you need to do is relink or update the shared library.


Porting Challenges

The major reason why this is possible, is also the major reason why porting an application to OpenSSL 1.1.0 can be difficult. Instead of having the contents of the structure directly visible in the header file, most of the internal C data structures were made opaque.

Take a look at the following example.

typedef struct hmac_ctx_st {     const EVP_MD *md;     EVP_MD_CTX *md_ctx;     EVP_MD_CTX *i_ctx;     EVP_MD_CTX *o_ctx;     unsigned int key_length;     unsigned char key[HMAC_MAX_MD_CBLOCK]; } HMAC_CTX;

When written like this, application code can do things like pull out the key and write it to a file -- not to be evil or insecure, but because it's possible to store things for later re-use.  And then create a new context on the stack and load it up with the old key:

HMAC_CTX h; memcpy(h.key, KeyDataFromSomewhere, sizeof h.key)

However, this is risky. It's far too easy to get the HMAC into an inconsistent state and ultimately get wrong results.


Instead, when the object is declared as opaque:

typedef struct hmac_ctx_st HMAC_CTX;

The code above will now fail:

% cat a.c #include "openssl/hmac.h" #include <string.h>

extern char key[];

foo() {     HMAC_CTX h;

    memcpy(h.key, key, sizeof h.key); }

% gcc -I$HOME/openssl/include -c a.c a.c: In function 'foo': a.c:8:14: error: storage size of 'h' isn't known      HMAC_CTX h;               ^


Fixing Your Code

Instead, the code must be written like this:

#include "openssl/hmac.h" #include <string.h>

extern char key[]; extern int keylen;

foo() {     HMAC_CTX *h = HMAC_CTX_new();

    HMAC_Init_ex(h, key, keylen, EVP_sha256(), NULL);     HMAC_CTX_free(h); }

Note that this is a more complete example: it guarantees all the fields are initialized and consistent, whereas the first one doesn't. The second example is also more future-proof: if OpenSSL adds new fields, the init call will still do the right thing.

Now this is admittedly a contrived example, but imagine if it was another structure like an RSA key or an SSL object. The chances of getting all the proper initialization done are pretty low, and all but guaranteed to be wrong in the next release.



As you can see, converting to OpenSSL 1.1.0 can be tedious, but it's mostly going to be a matter of looking at the manpages or header file, and finding the appropriate functions to get or set the fields that you used to directly manipulate. Fortunately, this kind of thing will be found at compile-time.