5.8 Using a Generic OFB Mode Implementation

5.8.1 Problem

You want a more high-level interface for OFB mode than your library provides. Alternatively, you want a portable OFB interface, or you have only a block cipher implementation and you would like to use OFB mode.

5.8.2 Solution

OFB mode encrypts by generating keystream, then combining the keystream with the plaintext via XOR. OFB generates keystream one block at a time. Each block of keystream is produced by encrypting the previous block of keystream, except for the first block, which is generated by encrypting the nonce.

Many libraries provide an OFB implementation. If you need code implementing this mode, you will find it in the following Section 5.8.3.

5.8.3 Discussion

You should probably use a higher-level abstraction, such as the one discussed in Recipe 5.16. Use a raw mode only when absolutely necessary, because there is a huge potential for introducing a security vulnerability by accident. If you still want to use OFB, be sure to use a message authentication code with it.

OFB mode is a stream-based mode. Encryption occurs by XOR'ing the keystream bytes with the plaintext bytes, as shown in Figure 5-3. The keystream is generated one block at a time, by encrypting the previous keystream block.[12] The first block is generated by encrypting the nonce.

[12] As with CFB mode, the "feedback size" could conceivably be smaller than the block size, but such schemes aren't secure.

Figure 5-3. OFB mode
figs/spcb_0503.gif

This mode shares many properties with counter mode (CTR), but CTR mode has additional benefits. OFB mode is therefore seeing less and less use these days. Of course, we recommend a higher-level mode than both of these modes, one that provides stronger security guarantees for example, CWC or CCM mode.

In Recipe 5.4, we discuss the advantages and drawbacks of OFB and compare it to other popular modes.

Many libraries already come with an implementation of OFB mode for any ciphers they support. However, some don't. For example, you may only get an implementation of the raw block cipher when you obtain reference code for a new cipher.

In the following sections we present a reasonably optimized implementation of OFB mode that builds upon the raw block cipher interface presented in Recipe 5.5. It also requires the spc_memset( ) function from Recipe 13.2.

5.8.3.1 The high-level API

This implementation has two APIs. The first is a high-level API, which takes a message as input and returns a dynamically allocated result.

unsigned char *spc_ofb_encrypt(unsigned char *key, size_t kl, unsigned char *nonce,                                unsigned char *in, size_t il); unsigned char *spc_ofb_decrypt(unsigned char *key, size_t kl, unsigned char *nonce,                                unsigned char *in, size_t il)

Both of these functions output the same number of bytes as were input, unless a memory allocation error occurs, in which case 0 is returned. The decryption routine is exactly the same as the encryption routine and is implemented by macro.

These two functions also erase the key from memory before exiting. You may want to have them erase the plaintext as well.

Here's the implementation of the interface:

#include <stdlib.h> #include <string.h>     unsigned char *spc_ofb_encrypt(unsigned char *key, size_t kl, unsigned char *nonce,                                unsigned char *in, size_t il) {   SPC_OFB_CTX       ctx;   unsigned char *out;       if (!(out = (unsigned char *)malloc(il))) return 0;   spc_ofb_init(&ctx, key, kl, nonce);   spc_ofb_update(&ctx, in, il, out);   spc_ofb_final(&ctx);   return out; }     #define spc_ofb_decrypt spc_ofb_encrypt

Note that the previous code depends on the SPC_OFB_CTX data type and the incremental OFB interface, both discussed in the following sections.

5.8.3.2 The incremental API

Let's look at the SPC_OFB_CTX data type. It's defined as:

typedef struct {   SPC_KEY_SCHED ks;   int           ix;   unsigned char nonce[SPC_BLOCK_SZ]; } SPC_OFB_CTX;

The ks field is an expanded version of the cipher key (block ciphers generally use a single key to derive multiple keys for internal use). The ix field is used to determine how much of the last block of keystream we have buffered (i.e., that hasn't been used yet). The nonce field is really the buffer in which we store the current block of the keystream.

To begin encrypting or decrypting, we need to initialize the mode. Initialization is the same operation for both encryption and decryption:

void spc_ofb_init(SPC_OFB_CTX *ctx, unsigned char *key, size_t kl, unsigned char                   *nonce) {   SPC_ENCRYPT_INIT(&(ctx->ks), key, kl);   spc_memset(key,0, kl);   memcpy(ctx->nonce, nonce, SPC_BLOCK_SZ);   ctx->ix = 0; }

Note again that we remove the key from memory during this operation.

Never use the same nonce (often called an IV in this context) twice with a single key. Use a secure random value or a counter. See Recipe 4.9 for more information on nonces.

Now we can add data as we get it using the spc_ofb_update( ) function. This function is particularly useful when a message arrives in pieces. You'll get the same results as if it all arrived at once. When you want to finish encrypting or decrypting, call spc_ofb_final( ).

You're responsible for making sure the init, update, and final calls do not happen out of order.

The function spc_ofb_update( ) has the following signature:

int spc_ofb_update(OFB_CTX *ctx, unsigned char *in, size_t il, unsigned char *out);

This function has the following arguments:

ctx

Pointer to the SPC_OFB_CTX object associated with the current message.

in

Pointer to a buffer containing the data to be encrypted or decrypted.

il

Number of bytes contained in the input buffer.

out

Pointer to the output buffer, which needs to be exactly as long as the input buffer.

Our implementation of this function always returns 1, but a hardware-based implementation might have an unexpected failure, so it's important to check the return value!

This API is in the spirit of PKCS #11, which provides a standard cryptographic interface to hardware. We do this so that the above functions can have the bulk of their implementations replaced with calls to PKCS #11-compliant hardware. PKCS #11 APIs generally pass out data explicitly indicating the length of data outputted, while we ignore that because it will always be zero on failure or the size of the input buffer on success. Also note that PKCS #11-based calls tend to order their arguments differently from the way we do, and they will not generally wipe key material, as we do in our initialization and finalization routines.

Because this API is developed with PKCS #11 in mind, it's somewhat more low-level than it needs to be, and therefore is a bit difficult to use properly. First, you need to be sure the output buffer is big enough to hold the input; otherwise, you will have a buffer overflow. Second, you need to make sure the out argument always points to the first unused byte in the output buffer. Otherwise, you will keep overwriting the same data every time spc_ofb_update( ) outputs.

Here's our implementation of spc_ofb_update( ):

int spc_ofb_update(SPC_OFB_CTX *ctx, unsigned char *in, size_t il, unsigned char                    *out) {   int i;       if (ctx->ix) {     while (ctx->ix) {       if (!il--) return 1;       *out++ = *in++ ^ ctx->nonce[ctx->ix++];       ctx->ix %= SPC_BLOCK_SZ;     }   }   if (!il) return 1;   while (il >= SPC_BLOCK_SZ) {     SPC_DO_ENCRYPT(&(ctx->ks), ctx->nonce, ctx->nonce);     for (i = 0;  i < SPC_BLOCK_SZ / sizeof(int);  i++)       ((int *)out)[i] = ((int *)in)[i] ^ ((int *)ctx->nonce)[i];     il  -= SPC_BLOCK_SZ;     in  += SPC_BLOCK_SZ;     out += SPC_BLOCK_SZ;   }   SPC_DO_ENCRYPT(&(ctx->ks), ctx->nonce, ctx->nonce);   for (i = 0;  i < il;  i++) *out++ = *in++ ^ ctx->nonce[ctx->ix++];   return 1; }

To finalize either encryption or decryption, use the spc_ofb_final( ) call, which never needs to output anything, because OFB is a streaming mode:

int spc_ofb_final(SPC_OFB_CTX *ctx) {   spc_memset(&ctx, 0, sizeof(SPC_OFB_CTX));   return 1; }

5.8.4 See Also

Recipe 4.9, Recipe 5.4, Recipe 5.5, Recipe 5.16, Recipe 13.2



Secure Programming Cookbook for C and C++
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2005
Pages: 266

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net