6.9 Checking Message Integrity
6.9.1 Problem
You want
to provide integrity for messages in such a way that people with a
secret key can verify that the message has not changed since the
integrity value (often called a tag) was first
calculated.
6.9.2 Solution
Use a message integrity check. As with hash functions, there are
somewhat standard interfaces, particularly an incremental interface.
6.9.3 Discussion
Libraries that support MACs tend to support incremental
operation using a standard structure, very similar to that used by
hash functions:
Allocate and key a context object. The context object holds the
internal state of the MAC until data processing is complete. The type
of the context object can be specific to the MAC, or there can be a
single type that works for all hash functions in a library. OpenSSL
supports only one MAC and has only the associated context type. The
key can be reused numerous times without reallocating. Often, you
will need to specify the underlying algorithm you are using for your
MAC. Reset the context object, setting the internal parameters of the MAC
to their initial state so that another message's
authentication tag can be calculated. Many MACs accept a nonce, and
this is where you would pass that in. This is often combined with the
"init" call when the algorithm does
not take a nonce, such as with OMAC and HMAC. "Update" the context object by
passing in data to be authenticated and the associated length of that
input. The results of the MAC'ing process will be
dependent on the order of the data that you pass, but you can pass in
all the partial data you wish. That is, calling the update routine
with the strings "he" then
"llo" would produce the same
results as calling it once with the string
"hello". The update function
generally takes as arguments the context object, the data to process,
and the associated length of that data. "Finalize" the context object and
produce the authentication tag. Most APIs will generally take as
arguments the context object and a buffer into which the message
digest is placed.
Often, you may have a block cipher or a hash function that
you'd like to turn into a MAC, but no associated
code comes with the cryptographic primitive. Alternately, you might
use a library such as OpenSSL or CryptoAPI that provides very narrow
choices. For this reason, the next several recipes provide
implementations of MACs we recommend for general-purpose use,
particularly OMAC, CMAC, and HMAC.
MACs are not quite as
low-level as cryptographic hash functions. Yet they are still fairly
low-level constructs, and there are some common pitfalls associated
with them. We discuss these elsewhere in the book, but
here's a summary of steps you should take to defend
yourself against common problems:
Don't use the same MAC key as an encryption key. If
you'd like to have a system with a single key, key
your MAC and encryption separately, using the technique from Recipe 4.11. Use a securely generated, randomly chosen key for your MAC, not
something hardcoded or otherwise predictable! Be sure to read Recipe 6.18 on how to use a
MAC and encryption together securely, as it can be difficult to do. Use an always-increasing nonce, and use this to actively thwart
capture replay attacks. Do this even if the MAC
doesn't have built-in support for nonces. (See
Recipe 6.21 for information on how to thwart
capture replay attacks, and Recipe 6.12 for
using a nonce with MACs that don't have direct
support for them.) It is of vital importance that any parties computing a MAC agree on
exactly what data is to be processed. To that end, it pays to get
very detailed in specifying the content of messages, including any
fields you have and how they are encoded before the MAC is computed.
Any encoding should be unambiguous.
|
Some MAC interfaces may not remove key
material from memory when done. Be sure to check the particular
implementation you're using.
OpenSSL provides only a single
MAC implementation, HMAC, while CryptoAPI supports both CBC-MAC and
HMAC. Neither quite follows the API outlined in this recipe, though
they stray in different ways. OpenSSL performs the reset operation
the same way as the initialization operation (you just pass in 0 in
place of the key and the algorithm arguments). CryptoAPI does not
allow resetting the context object, and instead requires that a
completely new context object be created.
OMAC and HMAC do not take a nonce by default. See Recipe 6.12 to see
how to use these algorithms with a nonce. To see how to use the
incremental HMAC interface in OpenSSL and CryptoAPI, see Recipe 6.10.
CryptoAPI does not have an all-in-one interface, but instead requires
use of its incremental API.
Most libraries also provide an all-in-one interface to the MACs they
provide. For example, the HMAC all-in-one function for OpenSSL looks
like this:
unsigned char *HMAC(const EVP_MD *evp_md, const void *key, int key_len,
const unsigned char *msg, int msglen, unsigned char *tag,
unsigned int *tag_len);
There is some variation in all-in-one APIs. Some are single-pass,
like the OpenSSL API described in this section. Others have a
separate initialization step and a context object, so that you do not
need to specify the underlying cryptographic primitive and rekey
every single time you want to use the MAC. That is, such interfaces
automatically call functions for resetting, updating, and
finalization for you.
6.9.4 See Also
Recipe 4.11, Recipe 6.10,
Recipe 6.12, Recipe 6.18, Recipe 6.21
|