5.17 Performing Block Cipher Setup (for CBC, CFB, OFB, and ECB Modes) in OpenSSL

5.17.1 Problem

You need to set up a cipher so that you can perform encryption and/or decryption operations in CBC, CFB, OFB, or ECB mode.

5.17.2 Solution

Here are the steps you need to perform for cipher setup in OpenSSL, using their high-level API:

  1. Make sure your code includes openssl/evp.h and links to libcrypto (-lcrypto).

  2. Decide which algorithm and mode you want to use, looking up the mode in Table 5-6 to determine which function instantiates an OpenSSL object representing that mode. Note that OpenSSL provides only a CTR mode implementation for AES. See Recipe 5.9 for more on CTR mode.

  3. Instantiate a cipher context (type EVP_CIPHER_CTX).

  4. Pass a pointer to the cipher context to EVP_CIPHER_CTX_init( ) to initialize memory properly.

  5. Choose an IV or nonce, if appropriate to the mode (all except ECB).

  6. Initialize the mode by calling EVP_EncryptInit_ex( ) or EVP_DecryptInit_ex( ), as appropriate:

    int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, ENGINE
                           *engine, unsigned char *key, unsigned char *ivornonce);
    int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, ENGINE
                           *engine, unsigned char *key, unsigned char *ivornonce);
  7. If desired, perform any additional configuration the cipher may allow (see Recipe 5.20).

5.17.3 Discussion

Use the raw OpenSSL API only when absolutely necessary because there is a huge potential for introducing a security vulnerability by accident. For general-purpose use, we recommend a high-level abstraction, such as that discussed in Recipe 5.16.

The OpenSSL EVP API is a reasonably high-level interface to a multitude of cryptographic primitives. It attempts to abstract out most algorithm dependencies, so that algorithms are easy to swap.[14]

[14] EVP stands for "envelope."

The EVP_EncryptInit_ex( ) and EVP_DecryptInit_ex( ) functions set up a cipher context object to be used for further operations. It takes four arguments that provide all the information necessary before encryption or decryption can begin. Both take the same arguments:

ctx

Pointer to an EVP_CIPHER_CTX object, which stores cipher state across calls.

type

Pointer to an EVP_CIPHER object, which represents the cipher configuration to use (see the later discussion).

engine

Pointer to an ENGINE object representing the actual implementation to use. For example, if you want to use hardware acceleration, you can pass in an ENGINE object that represents your cryptographic accelerator.

key

Pointer to the encryption key to be used.

ivornonce

Pointer to an initialization vector or none, if appropriate (use NULL otherwise). For CBC, CFB, and OFB modes, the initialization vector or nonce is always the same size as the block size of the cipher, which is often different from the key size of the cipher.

There are also deprecated versions of these calls, EVP_EncryptInit( ) and EVP_DecryptInit( ), that are the same except that they do not take the engine argument, and they use only the built-in software implementation.

Calling a function that returns an EVP_CIPHER object will cause the cipher's implementation to load dynamically and place information about the algorithm into an internal table if it has not yet done so. Alternatively, you can load all possible symmetric ciphers at once with a call to the function OpenSSL_add_all_ciphers( ), or all ciphers and message digest algorithms with a call to the function OpenSSL_add_all_algorithms( ) (neither function takes any arguments). For algorithms that have been loaded, you can retrieve pointers to their objects by name using the EVP_get_cipherbyname( ) function, which takes a single parameter of type char *, representing the desired cipher configuration.

Table 5-6 summarizes the possible functions that can load ciphers (if necessary) and return EVP_CIPHER objects. The table also shows the strings that can be used to look up loaded ciphers.

As noted in Recipe 5.2, we personally recommend AES-based solutions, or (of the ciphers OpenSSL offers) Triple-DES if AES is not appropriate. If you use other algorithms, be sure to research them thoroughly.

Table 5-6. Cipher instantiation reference

Cipher

Key strength / actual size (if different)

Cipher mode

Call for EVP_CIPHER object

Cipher lookup string

AES

128 bits

ECB

EVP_aes_128_ecb( )

aes-128-ecb

AES

128 bits

CBC

EVP_aes_128_cbc( )

aes-128-cbc

AES

128 bits

CFB

EVP_aes_128_cfb( )

aes-128-cfb

AES

128 bits

OFB

EVP_aes_128_ofb( )

aes-128-ofb

AES

192 bits

ECB

EVP_aes_192_ecb( )

aes-192-ecb

AES

192 bits

CBC

EVP_aes_192_cbc( )

aes-192-cbc

AES

192 bits

CFB

EVP_aes_192_cfb( )

aes-192-cfb

AES

192 bits

OFB

EVP_aes_192_ofb( )

aes-192-ofb

AES

256 bits

ECB

EVP_aes_256_ecb( )

aes-256-ecb

AES

256 bits

CBC

EVP_aes_256_cbc( )

aes-256-cbc

AES

256 bits

CFB

EVP_aes_256_cfb( )

aes-256-cfb

AES

256 bits

OFB

EVP_aes_256_ofb( )

aes-256-ofb

Blowfish

128 bits

ECB

EVP_bf_ecb( )

bf-ecb

Blowfish

128 bits

CBC

EVP_bf_cbc( )

bf-cbc

Blowfish

128 bits

CFB

EVP_bf_cfb( )

bf-cfb

Blowfish

128 bits

OFB

EVP_bf_ofb( )

bf-ofb

CAST5

128 bits

ECB

EVP_cast_ecb( )

cast-ecb

CAST5

128 bits

CBC

EVP_cast_cbc( )

cast-cbc

CAST5

128 bits

CFB

EVP_cast_cfb( )

cast-cfb

CAST5

128 bits

OFB

EVP_cast_ofb( )

cast-ofb

DES

Effective: 56 bitsActual: 64 bits

ECB

EVP_des_ecb( )

des-ecb

DES

Effective: 56 bitsActual: 64 bits

CBC

EVP_des_cbc( )

des-cbc

DES

Effective: 56 bitsActual: 64 bits

CFB

EVP_des_cfb( )

des-cfb

DES

Effective: 56 bitsActual: 64 bits

OFB

EVP_des_ofb( )

des-ofb

DESX

Effective[15]: 120 bitsActual: 128 bits

CBC

EVP_desx_cbc( )

desx

3-key Triple-DES

Effective: 112 bitsActual: 192 bits

ECB

EVP_des_ede3( )

des-ede3

3-key Triple-DES

Effective: 112 bitsActual: 192 bits

CBC

EVP_des_ede3_cbc( )

des-ede3-cbc

3-key Triple-DES

Effective: 112 bitsActual: 192 bits

CFB

EVP_des_ede3_cfb( )

des-ede3-cfb

3-key Triple-DES

Effective: 112 bitsActual: 192 bits

OFB

EVP_des_ede3_ofb( )

des-ede3-ofb

2-key Triple-DES

Effective: 112 bitsActual: 128 bits

ECB

EVP_des_ede( )

des-ede

2-key Triple-DES

Effective: 112 bitsActual: 128 bits

CBC

EVP_des_ede_cbc( )

des-ede-cbc

2-key Triple-DES

Effective: 112 bitsActual: 128 bits

CFB

EVP_des_ede_cfb( )

des-ede-cfb

2-key Triple-DES

Effective: 112 bitsActual: 128 bits

OFB

EVP_des_ede_ofb( )

des-ede-ofb

IDEA

128 bits

ECB

EVP_idea_ecb( )

idea-ecb

IDEA

128 bits

CBC

EVP_idea_cbc( )

idea-cbc

IDEA

128 bits

CFB

EVP_idea_cfb( )

idea-cfb

IDEA

128 bits

OFB

EVP_idea_ofb( )

idea-ofb

RC2™

128 bits

ECB

EVP_rc2_ecb( )

rc2-ecb

RC2™

128 bits

CBC

EVP_rc2_cbc( )

rc2-cbc

RC2™

128 bits

CFB

EVP_rc2_cfb( )

rc2-cfb

RC2™

128 bits

OFB

EVP_rc2_ofb( )

rc2-ofb

RC4™

40 bits

n/a

EVP_rc4_40( )

rc4-40

RC4™

128 bits

n/a

EVP_rc4( )

rc4

RC5™

128 bits

ECB

EVP_rc5_32_16_12_ecb( )

rc5-ecb

RC5™

128 bits

CBC

EVP_rc5_32_16_12_cbc( )

rc5-cbc

RC5™

128 bits

CFB

EVP_rc5_32_16_12_cfb( )

rc5-cfb

RC5™

128 bits

OFB

EVP_rc5_32_16_12_ofb( )

rc5-ofb

[15] There are known plaintext attacks against DESX that reduce the effective strength to 60 bits, but these are generally considered infeasible.

For stream-based modes (CFB and OFB), encryption and decryption are identical operations. Therefore, EVP_EncryptInit_ex( ) and EVP_DecryptInit_ex( ) are interchangeable in these cases.

While RC4 can be set up using these instructions, you must be very careful to set it up securely. We discuss how to do so in Recipe 5.23.

Here is an example of setting up an encryption context using 128-bit AES in CBC mode:

#include <openssl/evp.h>
#include <openssl/rand.h>
   
/* key must be of size EVP_MAX_KEY_LENGTH.
 * iv must be of size EVP_MAX_IV_LENGTH.
 */
EVP_CIPHER_CTX *sample_setup(unsigned char *key, unsigned char *iv) {
  EVP_CIPHER_CTX *ctx;
   
  /* This uses the OpenSSL PRNG .  See Recipe  11.9 */
  RAND_bytes(key, EVP_MAX_KEY_LENGTH);
  RAND_bytes(iv, EVP_MAX_IV_LENGTH);
  if (!(ctx = (EVP_CIPHER_CTX *)malloc(sizeof(EVP_CIPHER_CTX)))) return 0;
  EVP_CIPHER_CTX_init(ctx);
  EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(  ), 0, key, iv);
  return ctx;
}

This example selects a key and initialization vector at random. Both of these items need to be communicated to any party that needs to decrypt the data. The caller therefore needs to be able to recover this information. In this example, we handle this by having the caller pass in allocated memory, which we fill with the new key and IV. The caller can then communicate them to the other party in whatever manner is appropriate.

Note that to make replacing algorithms easier, we always create keys and initialization vectors of the maximum possible length, using macros defined in the openssl/evp.h header file.

5.17.4 See Also

Recipe 5.2, Recipe 5.9, Recipe 5.16, Recipe 5.18, Recipe 5.20, Recipe 5.23