Library Initialization

From OpenSSLWiki
Revision as of 06:42, 16 October 2013 by Jwalton (talk | contribs) (Added info on ENGINE_register_complete.)
Jump to navigationJump to search

This page discusses OpenSSL library initialization when using the libssl and libcrypto components.

Below is a list of some initialization calls you might encounter in code or documentation. Unfortunately, all the initialization function return a useless values (for example, always 1) or are void functions. There is no way to determine if a failure occurred.

  • SSL_library_init
  • OpenSSL_add_ssl_algorithms
  • OpenSSL_add_all_algorithms
  • SSL_load_error_strings
  • ERR_load_crypto_strings

libssl Initialization

libssl should be initialized with calls to OpenSSL_add_ssl_algorithms and SSL_load_error_strings. If your program is multi-threaded, you should install the static locks. If you need (or don't need) configuration from openssl.cnf, then you should call OPENSSL_config or OPENSSL_noconfig.

When you call libssl, the function will also initialize libcrypto components. There are two corner cases discussed in later sections. The first corner case is static locks, and second is OPENSSL_config.

OpenSSL_add_ssl_algorithms is a #define for SSL_library_init. You only need to call one or the other. If you want to print error strings using OpenSSL's built in functions, then call SSL_load_error_strings.

The SSL_library_init function loads the algorithms use by libssl. Below is an excerpt from ssl_algs.c (with some additional formatting for clarity).

int SSL_library_init(void)
{

#ifndef OPENSSL_NO_DES
    EVP_add_cipher(EVP_des_cbc());
    EVP_add_cipher(EVP_des_ede3_cbc());
#endif
#ifndef OPENSSL_NO_IDEA
    EVP_add_cipher(EVP_idea_cbc());
#endif
    ...

#ifndef OPENSSL_NO_COMP
    (void)SSL_COMP_get_compression_methods();
#endif

    ...

    /* initialize cipher/digest methods table */
    ssl_load_ciphers();

    return(1);
}

The call to ssl_load_ciphers simply builds a table for use in the library. The following is from ssl_ciph.c (with some additional formatting for clarity).

void ssl_load_ciphers(void)
{
    ssl_cipher_methods[SSL_ENC_DES_IDX] = EVP_get_cipherbyname(SN_des_cbc);
    ssl_cipher_methods[SSL_ENC_3DES_IDX] = EVP_get_cipherbyname(SN_des_ede3_cbc);
    ...
    ssl_digest_methods[SSL_MD_MD5_IDX] = EVP_get_digestbyname(SN_md5);
    ssl_mac_secret_size[SSL_MD_MD5_IDX] = EVP_MD_size(ssl_digest_methods[SSL_MD_MD5_IDX]);
    ...
    ssl_digest_methods[SSL_MD_SHA384_IDX] = EVP_get_digestbyname(SN_sha384);
    ssl_mac_secret_size[SSL_MD_SHA384_IDX] = EVP_MD_size(ssl_digest_methods[SSL_MD_SHA384_IDX]);
    ...
}

Library Apps

The following examines how the OpenSSL development team uses initialization in the OpenSSL utilities.

s_client initializes itself with the following calls:

  • OpenSSL_add_ssl_algorithms
  • SSL_load_error_strings

s_server initializes itself with the following calls:

  • SSL_load_error_strings();
  • OpenSSL_add_ssl_algorithms();

s_time initializes itself with the following calls:

  • OpenSSL_add_ssl_algorithms();

state_machine initializes itself with the following calls:

  • SSL_library_init();
  • OpenSSL_add_ssl_algorithms();
  • SSL_load_error_strings();
  • ERR_load_crypto_strings();

libcrypto Initialization

libcrypto should be initialized with calls to OpenSSL_add_all_algorithms and ERR_load_crypto_strings. If your program is multi-threaded, you should install the static locks. If you need (or don't need) configuration from openssl.cnf, then you should call OPENSSL_config or OPENSSL_noconfig.

The OPENSSL_add_all_algorithms function is #define'd to either OPENSSL_add_all_algorithms_conf or OPENSSL_add_all_algorithms_noconf depending upon the value of OPENSSL_LOAD_CONF. A typical installation does not define OPENSSL_LOAD_CONF, which means OPENSSL_add_all_algorithms_noconf is used. Below is an excerpt from c_all.c (with some additional formatting for clarity).

void OPENSSL_add_all_algorithms_noconf(void)
{
    /*
     * For the moment OPENSSL_cpuid_setup does something
     * only on IA-32, but we reserve the option for all
     * platforms...
     */
    OPENSSL_cpuid_setup();
    OpenSSL_add_all_ciphers();
    OpenSSL_add_all_digests();
    ...
}

OpenSSL_add_all_ciphers looks a lot like SSL_library_init from the libssl initialization routines (sans the call to ssl_load_ciphers). Below is an excerpt from c_allc.c (with some additional formatting for clarity).

void OpenSSL_add_all_ciphers(void)
{

#ifndef OPENSSL_NO_DES
    EVP_add_cipher(EVP_des_cfb());
    EVP_add_cipher(EVP_des_cfb1());
    EVP_add_cipher(EVP_des_cfb8());
    EVP_add_cipher(EVP_des_ede_cfb());
    EVP_add_cipher(EVP_des_ede3_cfb());
    EVP_add_cipher(EVP_des_ede3_cfb1());
    EVP_add_cipher(EVP_des_ede3_cfb8());
    ...
#endif

#ifndef OPENSSL_NO_RC4
    EVP_add_cipher(EVP_rc4());
    EVP_add_cipher(EVP_rc4_40());
# ifndef OPENSSL_NO_MD5
    EVP_add_cipher(EVP_rc4_hmac_md5());
# endif
#endif

    ...

    /* Note: there is no call to ssl_load_ciphers() here */
}

Finally, OpenSSL_add_all_algorithms(3) offers the following advice:

Calling OpenSSL_add_all_algorithms() links in all algorithms: as a result a statically linked executable can be quite large. If this is important it is possible to just add the required ciphers and digests.

If you want the small footprint, then call EVP_add_cipher with the ciphers and algorithms you need (and nothing more).

Library Apps

The following examines how the OpenSSL development team uses initialization in the OpenSSL utilities.

enc initializes itself with the following calls:

  • OpenSSL_add_all_algorithms();

dec initializes itself with the following calls:

  • OpenSSL_add_all_algorithms();

pkcs8 initializes itself with the following calls:

  • ERR_load_crypto_strings();
  • OpenSSL_add_all_algorithms();

cms_sign initializes itself with the following calls:

  • OpenSSL_add_all_algorithms();
  • ERR_load_crypto_strings();

cms_ver initializes itself with the following calls:

  • OpenSSL_add_all_algorithms();
  • ERR_load_crypto_strings();

Static Locks

If your program is multi-threaded, then you will need to install the static locks. The static locks are used for extensively for libssl, and used in the random number generator for libcrypto.

See threads(3) for details until the wiki is updated with an example.

OPENSSL_config

OPENSSL_config and OPENSSL_noconfig loads and unloads openssl.cnf. More correctly, a call to OPENSSL_config(NULL) loads the default configuration in openssl.cnf, OPENSSL_config(filename) loads another configuration, and OPENSSL_noconfig unlods a configuration.

OPENSSL_config may (or may not) be called depending upon how the OpenSSL library was configured, and it depends on whether OPENSSL_LOAD_CONF was defined. Because OPENSSL_config may (or may not) be called, your program may or may not need to make the call to OPENSSL_config. If, for example, your program is dynamically loading an ENGINE from OPENSSL_config, then you will need to ensure a call to OPENSSL_config.

You can check the value of OPENSSL_LOAD_CONF by cat'ing <openssl/opensslconf.h>. You can then decide to call OPENSSL_config or OPENSSL_noconfig based upon the definition (or lack threof) for OPENSSL_LOAD_CONF.

$ cat /usr/local/ssl/include/openssl/opensslconf.h | grep -i load
$

Here are the rules you should observe. In either case, your program should not depend upon the OpenSSL library and get into a known state.

  • If you need something from openssl.cnf, then call OPENSSL_config. Don't depend on the library to do it for you.
  • If you don't need something from openssl.cnf (or its mucking up you program), then call OPENSSL_noconfig. The library may have called OPENSSL_config for you.

Engines

If your application needs to use engines, then it should either call call ENGINE_load_builtin_engines or OPENSSL_config to load the built-in engines (including dynamically configured engines from openssl.cnf).

Engines are are automatically loaded (or not loaded) based on the definition of OPENSSL_LOAD_CONF (or lack of definition). You should not depend on library behavior, so you should call OPENSSL_config or ENGINE_load_builtin_engines if you need engines.

You can also load a particular engine if you know what you want to use. eng_all.c lists the built-in engines you can load. For example, the following loads the rdrand engine provided for some Intel CPUs.

unsigned long err = 0;
int rc = 0;

OPENSSL_cpuid_setup();
ENGINE_load_rdrand();

ENGINE* eng = ENGINE_by_id("rdrand");
if(NULL == eng) handleFailure();

rc = ENGINE_set_default(eng, ENGINE_METHOD_RAND);
if(1 != rc) handleFailure();

/* OK to proceed */
...

ENGINE_free(eng);
ENGINE_cleanup();

If you want an engine to provide all incumbent functionality for the OpenSSL library, then then call ENGINE_register_complete after loading the engine. Incumbent functionality is determined by the manufacturer and includes includes RSA, DSA, DH, ECDH, MD, and RAND operations. See eng_all.c, eng_fat.c, and engine(3) for details.

ENGINE* eng = ENGINE_by_id("XXX");
if(!(eng->flags & ENGINE_FLAGS_NO_REGISTER_ALL))
    ENGINE_register_complete(eng);

Errata