149
RC4 is a stream cipher and is blazingly fast compared to the available block ciphers in OpenSSL. Its certainly the fastest algorithm currently implemented in OpenSSL. RC4 is also well-regarded
as an algorithm. For this reason, and due to its widespread use in SSL, its vastly popular, though it is widely used with insecure 40-bit keys.
RC4 is difficult to use well. The encryption algorithm itself is good, but some problems with the way it sets up keys require care in using it. In particular, RSA Security recommends you take one
of the following two steps when using this algorithm:
1. Make sure that all key material is cryptographically hashed before use. The problem necessitating this solution is most prominent when frequently rekeying RC4. A common
approach to frequent rekeying is to use a base key, and then concatenate with a counter. In RC4, that turns out to be a bad thing to do. If you take the key material and the counter
and hash them together to get the actual key, the weakness goes away. The general recommendation of hashing all key material before use is a good one, no matter which
cipher you use in your applications.
2. Discard the first 256 bytes of the generated key stream before using it. The easy way to do this is to encrypt 256 bytes of random data and discard the results.
Additionally, as previously noted, it is particularly important to supplement use of RC4 with a MAC to ensure data integrity. See
Table 6-9 for more information.
Table 6-9. Referencing RC4 Key length
EVP call for cipher object String for cipher lookup
40 bits
EVP_rc4_40 rc4-40
128 bits
EVP_rc4 rc4
6.2.1.10 RC5™
RC5 is another block cipher from RSA Security. Its name is trademarked, and its algorithm is covered by an issued patent. You should certainly contact RSA Security before using this
algorithm in any application.
RC5 is interesting because it is fast, well-regarded, and highly customizable. According to the RC5 specification, you can choose 64- or 128-bit blocks, use keys up to 255 bytes in size, and can
use any number of cipher rounds, up to 255. However, OpenSSLs implementation uses 64-bit blocks and limits rounds to 8, 12, or 16, defaulting to 12. See
Table 6-10 .
Table 6-10. Referencing RC5 Cipher mode Key bits Rounds
EVP call for cipher object String for cipher lookup
ECB 128
12
EVP_rc5_32_16_12_ecb rc5-ecb
CBC 128 12
EVP_rc5_32_16_12_cbc rc5-cbc
CFB 128 12
EVP_rc5_32_16_12_cfb rc5-cfb
OFB 128 12
EVP_rc5_32_16_12_ofb rc5-ofb
Note that nondefault parameters to RC5 cannot currently be accessed through EVP calls or through cipher lookup by name. Instead, you must first reference the default RC5 cipher object in
the correct mode, and then use other calls to set parameters, as described below.
6.2.2 Initializing Symmetric Ciphers
Before we can begin encrypting or decrypting, we must allocate and initialize a cipher context. The cipher context is a data structure that keeps track of all relevant state for the purposes of
TE AM
FL Y
Team-Fly
®
150
encrypting or decrypting data over a period of time. For example, we can have multiple streams of data encrypted in CBC mode. The cipher context will keep track of the key associated with each
stream and the internal state that needs to be kept between messages for CBC mode. Additionally, when encrypting with a block-based cipher mode, the context object buffers data that doesnt
exactly align to the block size until more data arrives, or until the buffer is explicitly flushed, at which point the data is usually padded as appropriate.
[1] [1]
This happens only if padding is turned on, of course.
The generic cipher context type is
EVP_CIPHER_CTX
. We can initialize one, whether it was allocated dynamically or statically, by calling
EVP_CIPHER_CTX_init
, like so:
EVP_CIPHER_CTX x = EVP_CIPHER_CTX mallocsizeofEVP_CIPHER_CTX; EVP_CIPHER_CTX_initx;
After allocating the context object and initializing it, we must set up the cipher context. At this point, we generally determine whether the particular context will be used for encrypting or
decrypting. It is possible to set up a context to do both, but its a bad idea in any mode other than ECB mode, because race conditions can easily occur that will desynchronize communication.
Essentially, the internal cipher state of the two communicating parties needs to remain synchronized at all times. If both parties send data at the same time, they will likely end up trying
to decrypt the data they receive using an incorrect state.
When we set up a cipher context, we not only choose whether were encrypting or decrypting, but also do the following:
1. Choose the type of cipher we will be using, including the mode in which to use that cipher. We will be passing an
EVP_CIPHER
object to an initialization routine. 2. Set the key to be used for operations by passing it to the initialization routine as an array
of bytes. 3. Specify an initialization vector for the cipher, if appropriate for the mode. A default IV
will be used if not otherwise specified. 4. If using the engine release, we can specify whether we want to use hardware
acceleration, if available. If we do, we must have previously specified an engine to use that supports our hardware. Specifying
NULL
as an engine tells OpenSSL to use its default software implementations. In 0.9.7, this functionality will be part of the library
proper.
EVP_EncryptInit
is the preferred method for setting up a cipher context for encryption. For decryption, it is
EVP_DecryptInit
. Both of these methods have the same signature, which includes four parameters.
int EVP_EncryptInitEVP_CIPHER_CTX ctx, const EVP_CIPHER type, unsigned char key, unsigned char iv;
int EVP_DecryptInitEVP_CIPHER_CTX ctx, const EVP_CIPHER type, unsigned char key, unsigned char iv;
ctx The EVP cipher context object to use.
type The cipher to use.
key
151
The key to use for encrypting or decrypting. iv
The initialization vector to use. Notice that the engine package prefers an extended API,
EVP_EncryptInit_ex
and
EVP_DecryptInit_ex
, which inserts a fifth argument before the key that is a pointer to the engine to use; it should be
NULL
when no hardware acceleration is being used. When Version 0.9.7 of OpenSSL is released, these versions of the calls will be the preferred API. When using
engines, many calls can fail, so check error codes. We dont do this because we dont use the ENGINE API in our examples.
Lets consider an example in which we try to encrypt using Blowfish with 128-bit keys in CBC mode. CBC mode can use an initialization vector, which is always the size of one block in this
case, 8 bytes. We will use the OpenSSL pseudorandom number generator to provide a randomly generated key; however, distributing that key is not really covered in this example. For now, well
assume that you will do it offline, perhaps by exchanging a disk, or reading the key over the phone key exchange protocols are discussed in
Chapter 8 . To that end, we do print out the key to
stdout
in hexadecimal format. Note that doing this is not really the best idea for real applications. Example 6-1
shows how to do this. The following are the declarations for these functions.
int RAND_bytesunsigned char buf, int num; int RAND_pseudo_bytesunsigned char buf, int num;
The first of these functions writes num
bytes of cryptographically strong random bytes into the memory at
buf . The second function does precisely the same thing except that the random bytes
provided are not necessarily unpredictable. The latter function is not suitable for cryptographic needs it is no more secure than functions like
rand
. For more information on these functions, see the man page for
RAND_bytes
here.
Example 6-1. Preparing to use Blowfish in CBC mode for encryption
include opensslevp.h void select_random_keychar key, int b
{ int i;
RAND_byteskey, b; for i = 0; i b - 1; i++
printf02X:, key[i]; printf02X\n, key[b - 1];
} void select_random_ivchar iv, int b
{ RAND_pseudo_bytesiv, b;
} int setup_for_encryptionvoid
{
152
EVP_CIPHER_CTX ctx; char key[EVP_MAX_KEY_LENGTH];
char iv[EVP_MAX_IV_LENGTH]; if seed_prng
return 0; select_random_keykey, EVP_MAX_KEY_LENGTH;
select_random_iviv, EVP_MAX_IV_LENGTH; EVP_EncryptInitctx, EVP_bf_cbc, key, iv;
return 1; }
Note that multiple implementations of the
seed_prng
function are provided in Chapter 4
. It returns 0 if the pseudorandom number generator cannot be seeded securely. We return an error
status from our setup function in this case, so we dont need to check the return value of
RAND_pseudo_bytes
when we call it. Also, you may want to use raw entropy. See Chapter 4
for more information. Another thing to note is that the party decrypting the data will need to initialize its cipher context
with the same initialization vector created here. Passing the initialization vector in the clear is OK, but it should probably be MACd so that the receiver can detect tampering. If
NULL
is passed in for an IV, an array filled with zeros is used. Note that IVs can be used in all modes except ECB. In
ECB mode, you can still pass in an IV, but block ciphers will ignore it. Setting up for decryption is generally easier, because we already know the key and the IV used.
Example 6-2 shows how to set up for decryption under the same configuration.
Example 6-2. Preparing to use Blowfish in CBC mode for decryption
include opensslevp.h void setup_for_decryptionchar key, char iv
{ EVP_CIPHER_CTX ctx;
EVP_DecryptInitctx, EVP_bf_cbc, key, iv; }
Subsequent calls to
EVP_EncryptInit
or
EVP_DecryptInit
will change the value of any non-null parameter as long as the cipher type parameter is set to
NULL
. Otherwise, the context is completely reinitialized. Additionally, the key and the IV can both be set to
NULL
on the first call to these functions, and set separately later. This is necessary when you specify a cipher and then
change the key length from the default. Of course, you will need to at least provide a valid key before encryption begins.
6.2.3 Specifying Key Length and Other Options