6.21 Securely Authenticating a MAC (Thwarting Capture Replay Attacks)

6.21.1 Problem

You are using a MAC, and you need to make sure that when you get a message, you properly validate the MAC.

6.21.2 Solution

If you're using an ever-increasing nonce (which we strongly recommend), check to make sure that the nonce associated with the message is indeed larger than the last one. Then, of course, recalculate the MAC and check against the transmitted MAC.

6.21.3 Discussion

The following is an example of validating a MAC using the OMAC1 implementation in Recipe 6.11, along with AES-128. We nonce the MAC by using a 16-byte nonce as the first block of input, as discussed in Recipe 6.12. Note that we expect you to be MAC'ing the ciphertext, as discussed in Recipe 6.18.

#include <stdlib.h>
#include <string.h>
/* last_nonce must be a pointer to a NULL on first invocation. */
int spc_omac1_validate(unsigned char *ct, size_t ctlen, unsigned char sent_nonce[16], 
                       unsigned char *sent_tag, unsigned char *k, 
                       unsigned char **last_nonce) {
  int           i;
  SPC_OMAC_CTX      c;
  unsigned char calc_tag[16]; /* Maximum tag size for OMAC. */
  spc_omac1_init(&c, k, 16);
  if (*last_nonce) {
    for (i = 0;  i < 16;  i++)
      if (sent_nonce[i] > (*last_nonce)[i]) goto nonce_okay;
    return 0; /* Nonce is equal to or less than last nonce. */
  spc_omac_update(&c, sent_nonce, 16);
  spc_omac_update(&c, ct, ctlen);
  spc_omac_final(&c, calc_tag);
  for (i = 0;  i < 16;  i++)
    if (calc_tag[i] != sent_tag[i]) return 0;
  if (sent_nonce) {
    if (!*last_nonce) *last_nonce = (unsigned char *)malloc(16);
    if (!*last_nonce) abort();  /* Consider an exception instead. */
    memcpy(*last_nonce, sent_nonce, 16);
  return 1;

This code requires you to pass in a char ** to track the last nonce that was received. You're expected to allocate your own char *, set it to NULL, and pass in the address of that char *. The validate function will update that memory with the last valid nonce it saw, so that it can check the new nonce against the last nonce to make sure it got bigger. The function will return 1 if the MAC validates; otherwise, it will return 0.

6.21.4 See Also

Recipe 6.11, Recipe 6.12, Recipe 6.18