OpenSSL 1.1.0 Changes

From OpenSSLWiki
Jump to navigationJump to search

This page discusses the API changes for OpenSSL version 1.1. The overall goal of the API changes is to make many data structures opaque to applications. Data hiding provides a number of benefits:

  • Fields can be changed without breaking binary compatibility
  • Applications are more robust and can be more assured about correctness
  • It helps determine which (new) accessors and settors, for example, are needed

Applications which support both OpenSSL 1.0.2 (and below) and OpenSSL 1.1.0 (and above) should visit the section Compatibility Layer below. The Compatibility Layer provides OpenSSL 1.1.0 functions, like RSA_get0_key, to OpenSSL 1.0.2 clients. The source code is available for download below

If you find your library or program used to work with OpenSSL 1.0.2 but no longer works with OpenSSL 1.1.0, then please add details to discussion below at Things that no longer work.

Major Changes so far[edit]

  • All structures in libssl public header files have been removed so that they are "opaque" to library users. You should use the provided accessor functions instead
  • The old DES API has been removed
  • bn, a sub library in libcrypto, has been made opaque
  • Access to deprecated functions/macros has been removed by default. To enable access you must do two things. 1) Build OpenSSL with deprecation support (pass "enable-deprecated" as an argument to config) 2) Applications must define "OPENSSL_USE_DEPRECATED" before including OpenSSL header files
  • HMAC_Init and HMAC_cleanup were previously stated in the docs and header files as being deprecated - but were not flagged in previous versions with OPENSSL_NO_DEPRECATED. This has been corrected in 1.1.0. Access to these functions/macros will be off by default in 1.1.0 as per the note above about deprecation.

OPENSSL_API_COMPAT[edit]

Various functions get deprecated as other interfaces get added, but are still available in a default build. The include files support setting the OPENSSL_API_COMPAT define that will hide functions that are deprecated in the selected version. To select the 1.1.0 version use -DOPENSSL_API_COMPAT=0x10100000L.

Backward compatibility[edit]

Since some structures have become opaque you can't directly access the member any more. You might need to create backward compatible macros or functions if you still want to support older versions of OpenSSL. A suggested way of doing that is:

 #if OPENSSL_VERSION_NUMBER < 0x10100000L
 #define OBJ_get0_data(o) ((o)->data)
 #define OBJ_length(o) ((o)->length)
 #endif

Compatibility Layer[edit]

Application code now has to use pointers, and cannot allocate objects directly on the stack. For instance if the old code did:

    BN_CTX ctx;

You now have to do:

    BN_CTX *ctx;

    ctx = BN_CTX_new();
    [...]
    BN_CTX_free(ctx);

If you want to access rsa->n you now have to do something like:

    const BIGNUM *n;

    RSA_get0_key(rsa, &n, NULL, NULL);

There are new functions available for to get and set such variables that are not available in older versions. The suggested way to solve this is add your own copy of the new functions based on the one in OpenSSL 1.1.0. Here is an overview of most of them and how they could look like:

 #if OPENSSL_VERSION_NUMBER < 0x10100000L

 #include <string.h>
 #include <openssl/engine.h>

 static void *OPENSSL_zalloc(size_t num)
 {
    void *ret = OPENSSL_malloc(num);

    if (ret != NULL)
        memset(ret, 0, num);
    return ret;
 }

 int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
 {
    /* If the fields n and e in r are NULL, the corresponding input
     * parameters MUST be non-NULL for n and e.  d may be
     * left NULL (in case only the public key is used).
     */
    if ((r->n == NULL && n == NULL)
        || (r->e == NULL && e == NULL))
        return 0;

    if (n != NULL) {
        BN_free(r->n);
        r->n = n;
    }
    if (e != NULL) {
        BN_free(r->e);
        r->e = e;
    }
    if (d != NULL) {
        BN_free(r->d);
        r->d = d;
    }

    return 1;
 }

 int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q)
 {
    /* If the fields p and q in r are NULL, the corresponding input
     * parameters MUST be non-NULL.
     */
    if ((r->p == NULL && p == NULL)
        || (r->q == NULL && q == NULL))
        return 0;

    if (p != NULL) {
        BN_free(r->p);
        r->p = p;
    }
    if (q != NULL) {
        BN_free(r->q);
        r->q = q;
    }

    return 1;
 }

 int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp)
 {
    /* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input
     * parameters MUST be non-NULL.
     */
    if ((r->dmp1 == NULL && dmp1 == NULL)
        || (r->dmq1 == NULL && dmq1 == NULL)
        || (r->iqmp == NULL && iqmp == NULL))
        return 0;

    if (dmp1 != NULL) {
        BN_free(r->dmp1);
        r->dmp1 = dmp1;
    }
    if (dmq1 != NULL) {
        BN_free(r->dmq1);
        r->dmq1 = dmq1;
    }
    if (iqmp != NULL) {
        BN_free(r->iqmp);
        r->iqmp = iqmp;
    }

    return 1;
 }

 void RSA_get0_key(const RSA *r,
                  const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
 {
    if (n != NULL)
        *n = r->n;
    if (e != NULL)
        *e = r->e;
    if (d != NULL)
        *d = r->d;
 }

 void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
 {
    if (p != NULL)
        *p = r->p;
    if (q != NULL)
        *q = r->q;
 }

 void RSA_get0_crt_params(const RSA *r,
                         const BIGNUM **dmp1, const BIGNUM **dmq1,
                         const BIGNUM **iqmp)
 {
    if (dmp1 != NULL)
        *dmp1 = r->dmp1;
    if (dmq1 != NULL)
        *dmq1 = r->dmq1;
    if (iqmp != NULL)
        *iqmp = r->iqmp;
 }

 void DSA_get0_pqg(const DSA *d,
                  const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
 {
    if (p != NULL)
        *p = d->p;
    if (q != NULL)
        *q = d->q;
    if (g != NULL)
        *g = d->g;
 }

 int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
 {
    /* If the fields p, q and g in d are NULL, the corresponding input
     * parameters MUST be non-NULL.
     */
    if ((d->p == NULL && p == NULL)
        || (d->q == NULL && q == NULL)
        || (d->g == NULL && g == NULL))
        return 0;

    if (p != NULL) {
        BN_free(d->p);
        d->p = p;
    }
    if (q != NULL) {
        BN_free(d->q);
        d->q = q;
    }
    if (g != NULL) {
        BN_free(d->g);
        d->g = g;
    }

    return 1;
 }

 void DSA_get0_key(const DSA *d,
                  const BIGNUM **pub_key, const BIGNUM **priv_key)
 {
    if (pub_key != NULL)
        *pub_key = d->pub_key;
    if (priv_key != NULL)
        *priv_key = d->priv_key;
 }

 int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
 {
    /* If the field pub_key in d is NULL, the corresponding input
     * parameters MUST be non-NULL.  The priv_key field may
     * be left NULL.
     */
    if (d->pub_key == NULL && pub_key == NULL)
        return 0;

    if (pub_key != NULL) {
        BN_free(d->pub_key);
        d->pub_key = pub_key;
    }
    if (priv_key != NULL) {
        BN_free(d->priv_key);
        d->priv_key = priv_key;
    }

    return 1;
 }

 void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
 {
    if (pr != NULL)
        *pr = sig->r;
    if (ps != NULL)
        *ps = sig->s;
 }

 int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
 {
    if (r == NULL || s == NULL)
        return 0;
    BN_clear_free(sig->r);
    BN_clear_free(sig->s);
    sig->r = r;
    sig->s = s;
    return 1;
 }

 void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
 {
    if (pr != NULL)
        *pr = sig->r;
    if (ps != NULL)
        *ps = sig->s;
 }

 int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
 {
    if (r == NULL || s == NULL)
        return 0;
    BN_clear_free(sig->r);
    BN_clear_free(sig->s);
    sig->r = r;
    sig->s = s;
    return 1;
 }

 void DH_get0_pqg(const DH *dh,
                 const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
 {
    if (p != NULL)
        *p = dh->p;
    if (q != NULL)
        *q = dh->q;
    if (g != NULL)
        *g = dh->g;
 }

 int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
 {
    /* If the fields p and g in d are NULL, the corresponding input
     * parameters MUST be non-NULL.  q may remain NULL.
     */
    if ((dh->p == NULL && p == NULL)
        || (dh->g == NULL && g == NULL))
        return 0;

    if (p != NULL) {
        BN_free(dh->p);
        dh->p = p;
    }
    if (q != NULL) {
        BN_free(dh->q);
        dh->q = q;
    }
    if (g != NULL) {
        BN_free(dh->g);
        dh->g = g;
    }

    if (q != NULL) {
        dh->length = BN_num_bits(q);
    }

    return 1;
 }

 void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
 {
    if (pub_key != NULL)
        *pub_key = dh->pub_key;
    if (priv_key != NULL)
        *priv_key = dh->priv_key;
 }

 int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
 {
    /* If the field pub_key in dh is NULL, the corresponding input
     * parameters MUST be non-NULL.  The priv_key field may
     * be left NULL.
     */
    if (dh->pub_key == NULL && pub_key == NULL)
        return 0;

    if (pub_key != NULL) {
        BN_free(dh->pub_key);
        dh->pub_key = pub_key;
    }
    if (priv_key != NULL) {
        BN_free(dh->priv_key);
        dh->priv_key = priv_key;
    }

    return 1;
 }

 int DH_set_length(DH *dh, long length)
 {
    dh->length = length;
    return 1;
 }

 const unsigned char *EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX *ctx)
 {
    return ctx->iv;
 }

 unsigned char *EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX *ctx)
 {
    return ctx->iv;
 }

 EVP_MD_CTX *EVP_MD_CTX_new(void)
 {
    return OPENSSL_zalloc(sizeof(EVP_MD_CTX));
 }

 void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
 {
    EVP_MD_CTX_cleanup(ctx);
    OPENSSL_free(ctx);
 }

 RSA_METHOD *RSA_meth_dup(const RSA_METHOD *meth)
 {
    RSA_METHOD *ret;

    ret = OPENSSL_malloc(sizeof(RSA_METHOD));

    if (ret != NULL) {
        memcpy(ret, meth, sizeof(*meth));
        ret->name = OPENSSL_strdup(meth->name);
        if (ret->name == NULL) {
            OPENSSL_free(ret);
            return NULL;
        }
    }

    return ret;
 }

 int RSA_meth_set1_name(RSA_METHOD *meth, const char *name)
 {
    char *tmpname;

    tmpname = OPENSSL_strdup(name);
    if (tmpname == NULL) {
        return 0;
    }

    OPENSSL_free((char *)meth->name);
    meth->name = tmpname;

    return 1;
 }

 int RSA_meth_set_priv_enc(RSA_METHOD *meth,
                          int (*priv_enc) (int flen, const unsigned char *from,
                                           unsigned char *to, RSA *rsa,
                                           int padding))
 {
    meth->rsa_priv_enc = priv_enc;
    return 1;
 }

 int RSA_meth_set_priv_dec(RSA_METHOD *meth,
                          int (*priv_dec) (int flen, const unsigned char *from,
                                           unsigned char *to, RSA *rsa,
                                           int padding))
 {
    meth->rsa_priv_dec = priv_dec;
    return 1;
 }

 int RSA_meth_set_finish(RSA_METHOD *meth, int (*finish) (RSA *rsa))
 {
    meth->finish = finish;
    return 1;
 }

 void RSA_meth_free(RSA_METHOD *meth)
 {
    if (meth != NULL) {
        OPENSSL_free((char *)meth->name);
        OPENSSL_free(meth);
    }
 }

 int RSA_bits(const RSA *r)
 {
    return (BN_num_bits(r->n));
 }

 RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
 {
    if (pkey->type != EVP_PKEY_RSA) {
        return NULL;
    }
    return pkey->pkey.rsa;
 }


 HMAC_CTX *HMAC_CTX_new(void)
 {
    HMAC_CTX *ctx = OPENSSL_malloc(sizeof(*ctx));
    if (ctx != NULL) {
        if (!HMAC_CTX_reset(ctx)) {
            HMAC_CTX_free(ctx);
            return NULL;
        }
    }
    return ctx;
 }

 void HMAC_CTX_free(HMAC_CTX *ctx)
 {
    if (ctx != NULL) {
        hmac_ctx_cleanup(ctx);
        EVP_MD_CTX_free(ctx->i_ctx);
        EVP_MD_CTX_free(ctx->o_ctx);
        EVP_MD_CTX_free(ctx->md_ctx);
        OPENSSL_free(ctx);
    }
 }
 #endif

No longer works[edit]

This section details some of the programs nd libraries that used to work with OpenSSL 1.0.2 (and below), but no longer work with OpenSSL 1.1.0 (and above).

Qt[edit]

Here's what's broken in the dev branch of Qt when building openssl master as of 6 Feb 2015.

  • DH - we were directly accessing p and q to set the DH params to primes embedded in Qt. We can probably replace this with SSL_CTX_set_dh_auto(ctx, 1). Another option suggested by Steve Henson is to save the DHparams we're using at the moment then use d2i_DHparams to load them in. This is compatible with openssl versions that don't have the dh_auto option.
  • ctx->cert_store - we were directly accessing the cert_store field of SSL_CTX. We can probably replace this with X509_STORE *SSL_CTX_get_cert_store(SSL_CTX *ctx) [Fixed in dev]
  • session->tlsext_tick_lifetime_hint - we were directly accessing the lifetime hint of the session. [A new API to access this field has been added]
  • cipher->valid - we were directly accessing the valid field of SSL_CIPHER. No replacement found. [This turned out not to be needed and so will be removed].

cURL[edit]

  • SSL_SESSION->ssl_version. Replaced with SSL_version(SSL *)

wget[edit]

  • SSL->state. Replaced with SSL_state(SSL *)

Apache Traffic Manager[edit]

  • Setting SSL->rbio without setting SSL->wbio. New function introduction in 1.1.0 to handle this: SSL_set_rbio()

OpenConnect[edit]

In order to simulate "resume" of a DTLS session which never really existed but which was actually negotiated over the VPN control connection, this code in the OpenConnect VPN client needs to set the following fields in a new SSL_SESSION:

  • ->ssl_version
  • ->cipher{,_id}
  • ->master_key{,_length}
  • ->session_id{,_length}

This was fixed with this OpenConnect commit which makes it create the ASN.1 representation of the session and import it with d2i_SSL_SESSION(). This is done conditionally in the above patch because it depends on the fix in openssl HEAD for d2i_SSL_SESSION() to make it cope with DTLS1_BAD_VER (RT#3704).

Other simpler things which broke:

  • SSL_CIPHER->id. Replaced with SSL_CIPHER_get_id()
  • SSL_CTX->extra_certs. Replaced with SSL_CTX_get_extra_chain_certs_only()

TianoCore/EDKII[edit]

EDKII is the reference implementation of UEFI firmware.

  • Various implicit inclusions of <openssl/bn.h> and <openssl/rsa.h> needed to be made explicit. (commit)
  • X509_NAME->bytes->{data,length}. Replaced with i2d_X509_NAME() (commit)
  • X509_ATTRIBUTE->{object,value}. Replaced with X509_ATTRIBUTE_get0_object() and X509_ATTRIBUTE_get0_type() (commit)
  • ASN1_OBJECT->{length,data}. Replaced with OBJ_get0_data() and OBJ_length(). With backward-compatibility #define of same. (commit)

OpenSSH[edit]

OpenSSH does not compile against OpenSSL 1.1.0. A patch was provided at Pull Request #48, Make it build using OpenSSL 1.1.0 in September 2016, but it was not acted upon.

Downloads[edit]

openssl-compat.tar.gz - openssl-compat.tar.gz includes sources files openssl-compat.h and openssl-compat.c. The files provide the OpenSSL 1.1.0 compatibility layer for OpenSSL 1.0.2 and below users. OpenSSL 1.0.2 users should add openssl-compat.h and openssl-compat.c to their project, and then access data members through the functions.