154
Another desirable option to set in a cipher context is whether padding is used. Without padding, the size of the ciphertext will always be the same size as the plaintext. On the downside, the length
of the data encrypted must be an exact multiple of the block size. With padding, any length in bytes is feasible, but the resulting ciphertext can be up to a block longer than the plaintext.
Unfortunately, OpenSSL versions through 0.9.6c do not allow padding to be disabled. This changes in Version 0.9.7, which has a function called
EVP_CIPHER_CTX_set_padding
that takes a pointer to a cipher context, and an integer that represents a Boolean value 0 for no
padding, 1 for padding.
6.2.4 Encryption
Once the c ipher context is initialized, there are two steps to encryption with the EVP interface: updating
and finalization. When you have data to encrypt, you pass the data to the update function, along with a pointer to where youd like any output to go. There may or may not be output as the
result of an update. If the cipher can encrypt one or more entire blocks of data, it will do so. Any leftover data will be buffered and processed either during the next call to the update function or
during the call to finalize. When calling finalize, any leftover data is padded and encrypted. If there is no leftover data, a block of pad is encrypted.
[2]
As with updating, you must tell the routine where to store resulting data.
[2]
For the curious, here is how standard PKCS padding works. If n bytes of padding are needed, then n is used as the value of each byte. For example, the value of a one-byte pad expressed in
hexadecimal is 0x01, and the value of an eight-byte pad is 0x0808080808080808. This way, the extent of the pad can be calculated unambiguously by looking at the last byte of the padded
plaintext.
The update function is
EVP_EncryptUpdate
.
int EVP_EncryptUpdateEVP_CIPHER_CTX ctx, unsigned char out, int outl,
unsigned char in, int inl;
ctx The cipher context to use.
out A buffer that will receive the encrypted data.
outl Receives the number of bytes written to the encrypted data buffer.
in A buffer that contains the data to be encrypted.
inl Specifies the number of bytes contained in the input data buffer.
When using a block-based cipher mode ECB or CBC, the amount of output written can be both larger and smaller than the length of the input, due to internal buffering and padding. If youre
using a cipher with 8-byte 64-bit blocks, the output could be up to 7 bytes smaller than the input, or up to 7 bytes larger.
[3]
If you are encrypting incrementally with a single key, and are producing
155
packets of data, this is good to keep in mind. If, instead, youre encrypting to a single buffer, you will always avoid overflow by making the output buffer an entire block bigger than the input
buffer the extra block may fill with padding. Optionally, you can manually keep track of exactly how much data will be output as a function of how much data was input.
[3]
Actually, the current implementation limits the output to six bytes longer than the input. However, you should not count on that behavior.
The finalization function is
EVP_EncryptFinal
.
int EVP_EncryptFinalEVP_CIPHER_CTX ctx, unsigned char out, int outl;
ctx The cipher context to use.
out A buffer that will receive the encrypted data.
outl Receives the number of bytes written to the encrypted data buffer.
Currently, this function always outputs. However, in the forthcoming 0.9.7 release it will not place anything in the output buffer at all if padding is turned off. In such a case, if there is any buffered
data, the function returns an error. Additionally,Version 0.9.7 adds an
EVP_EncryptFinal_ex
call that should be used when a context has been initialized by
EVP_EncryptInit_ex
. Example 6-3
shows the implementation of a function that takes a pointer to an initialized EVP cipher context, a buffer to encrypt, an associated length, and a pointer to an integer. The function
then encrypts the data 100 bytes at a time into a heap-allocated buffer, which is the functions return value. The length of the resulting ciphertext is passed back in the address specified by the
final parameter.
Example 6-3. Encrypting plaintext 100 bytes at a time
include opensslevp.h char encrypt_exampleEVP_CIPHER_CTX ctx, char data, int inl, int
rb {
char ret; int i, tmp, ol;
ol = 0; ret = char mallocinl + EVP_CIPHER_CTX_block_sizectx;
for i = 0; i inl 100; i++ {
EVP_EncryptUpdatectx, ret[ol], tmp, data[ol], 100; ol += tmp;
} if inl 100
{ EVP_EncryptUpdatectx, ret[ol], tmp, data[ol], inl100;
ol += tmp; }
156
EVP_EncryptFinalctx, ret[ol], tmp; rb = ol + tmp;
return ret; }
Factoring in the block length done by calling
EVP_CIPHER_CTX_block_size
is unnecessary when using a stream cipher, or when using the CFB or OFB cipher modes, since there is no
padding in those cases. As a result, the cipher can output encrypted data as needed, without having to buffer any plaintext.
[4] [4]
Strictly speaking, this isnt entirely true in CFB mode because the first block can be buffered.
The above example works well when we can afford to encrypt everything into a single buffer before processing the ciphertext. It doesnt work so well if we need to deal with ciphertext
incrementally. For example, we might wish to send blocks of data as quickly as possible, and not wait for all data to be processed.
Example 6-4 shows a solution for such a scenario. Data to be
encrypted is sent to
incremental_encrypt
as needed. When theres data to be sent,
incremental_encrypt
calls
incremental_send
, which is simply a stub, but it can place those blocks currently encrypted on the network. When all data to be encrypted has been passed to
incremental_encrypt
, then
incremental_finish
is called.
Example 6-4. Performing incremental encryption
include opensslevp.h int incremental_encryptEVP_CIPHER_CTX ctx, char data, int inl
{ char buf;
int ol; int bl = EVP_CIPHER_CTX_block_sizectx;
Up to the block size - 1 chars can be buffered up. Add that to the length
of the input, and then we can easily determine the maximum number of
blocks output will take by integer divison with the block size. buf = char mallocinl + bl - 1 bl bl;
EVP_EncryptUpdatectx, buf, ol, data, inl; if ol
incremental_sendbuf, ol; incremental_send must copy if it wants to store.
freebuf; return ol;
} Also returns the number of bytes written.
int incremental_finishEVP_CIPHER_CTX ctx {
char buf; int ol;
buf = char mallocEVP_CIPHER_CTX_block_sizectx; EVP_EncryptFinalctx, buf, ol;
if ol incremental_sendbuf, ol;
freebuf; return ol;
}
157
Note that the number of bytes written by
EVP_EncryptFinal
should always be 8 when using 64-bit blocks and when padding is enabled.
6.2.5 Decryption