[ Team LiB ] Previous Section Next Section

6.10 Using HMAC

6.10.1 Problem

You want to provide message authentication using HMAC.

6.10.2 Solution

If you are using OpenSSL, you can use the HMAC API:

/* The incremental interface */
void HMAC_Init(HMAC_CTX *ctx, const void *key, int len, const EVP_MD *md);
void HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, int len);
void HMAC_Final(HMAC_CTX *ctx, unsigned char *tag, unsigned int *tag_len);
   
/* HMAC_cleanup erases the key material from memory. */
void HMAC_cleanup(HMAC_CTX *ctx); 
   
/* The all-in-one interface. */
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);

If you are using CryptoAPI, you can use the CryptCreateHash( ), CryptHashData( ), CryptGetHashParam( ), CryptSetHashParam( ), and CryptDestroyHash( ) functions:

BOOL WINAPI CryptCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey,
                            DWORD dwFlags, HCRYPTHASH *phHash);
BOOL WINAPI CryptHashData(HCRYPTHASH hHash, BYTE *pbData, DWORD cbData,
                          DWORD dwFlags);
BOOL WINAPI CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData,
                              DWORD *pcbData, DWORD dwFlags);
BOOL WINAPI CryptSetHashParam(HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData,
                              DWORD dwFlags);
BOOL WINAPI CryptDestroyHash(HCRYPTHASH hHash);

Otherwise, you can use the HMAC implementation provided with this recipe in combination with any cryptographic hash function you have handy.

6.10.3 Discussion

Be sure to look at our generic recommendations for using a MAC (Recipe 6.9).

Here's an example of using OpenSSL's incremental interface to hash two messages using SHA1:

#include <stdio.h>
#include <openssl/hmac.h>
   
void spc_incremental_hmac(unsigned char *key, size_t keylen) {
  int           i;
  HMAC_CTX      ctx;
  unsigned int  len;
  unsigned char out[20];
   
  HMAC_Init(&ctx, key, keylen, EVP_sha1(  ));
  HMAC_Update(&ctx, "fred", 4);
  HMAC_Final(&ctx, out, &len);
  for (i = 0;  i < len;  i++) printf("%02x", out[i]);
  printf("\n");
   
  HMAC_Init(&ctx, 0, 0, 0);
  HMAC_Update(&ctx, "fred", 4);
  HMAC_Final(&ctx, out, &len);
  for (i = 0;  i < len;  i++) printf("%02x", out[i]);
  printf("\n");
  HMAC_cleanup(&ctx); /* Remove key from memory */
}

To reset the HMAC context object, we call HMAC_Init( ), passing in zeros (NULLs) in place of the key, key length, and digest type to use. The NULL argument when initializing in OpenSSL generally means "I'm not supplying this value right now; use what you already have."

The following example shows an implementation of the same code provided for OpenSSL, this time using CryptoAPI (with the exception of resetting the context, because CryptoAPI actually requires a new one to be created). This implementation requires the use of the code in Recipe 5.26 to convert raw key data into an HCRYPTKEY object as required by CryptCreateHash( ). Note the difference in the arguments required between spc_incremental_hmac( ) as implemented for OpenSSL, and SpcIncrementalHMAC( ) as implemented for CryptoAPI. The latter requires an additional argument that specifies the encryption algorithm for the key. Although the information is never really used, CryptoAPI insists on tying an encryption algorithm to key data. In general, CALG_RC4 should work fine for arbitrary key data (the value will effectively be ignored).

#include <windows.h>
#include <wincrypt.h>
#include <stdio.h>
   
void SpcIncrementalHMAC(BYTE *pbKey, DWORD cbKey, ALG_ID Algid) {
  BYTE       out[20];
  DWORD      cbData = sizeof(out), i;
  HCRYPTKEY  hKey;
  HMAC_INFO  HMACInfo;
  HCRYPTHASH hHash;
  HCRYPTPROV hProvider;
   
  hProvider = SpcGetExportableContext(  );
  hKey = SpcImportKeyData(hProvider, Algid, pbKey, cbKey);
  CryptCreateHash(hProvider, CALG_HMAC, hKey, 0, &hHash);
   
  HMACInfo.HashAlgid     = CALG_SHA1;
  HMACInfo.pbInnerString = HMACInfo.pbOuterString = 0;
  HMACInfo.cbInnerString = HMACInfo.cbOuterString = 0;
  CryptSetHashParam(hHash, HP_HMAC_INFO, (BYTE *)&HMACInfo, 0);
   
  CryptHashData(hHash, (BYTE *)"fred", 4, 0);
  CryptGetHashParam(hHash, HP_HASHVAL, out, &cbData, 0);
  for (i = 0;  i < cbData;  i++) printf("%02x", out[i]);
  printf("\n");
   
  CryptDestroyHash(hHash);
  CryptDestroyKey(hKey);
  CryptReleaseContext(hProvider, 0);
}

If you aren't using OpenSSL or CryptoAPI, but you have a hash function that you'd like to use with HMAC, you can use the following HMAC implementation:

#include <stdlib.h>
#include <string.h>

typedef struct {
  DGST_CTX      mdctx;
  unsigned char inner[DGST_BLK_SZ];
  unsigned char outer[DGST_BLK_SZ];
} SPC_HMAC_CTX;
   
void SPC_HMAC_Init(SPC_HMAC_CTX *ctx, unsigned char *key, size_t klen) {
  int           i;
  unsigned char dk[DGST_OUT_SZ];
   
  DGST_Init(&(ctx->mdctx));
  memset(ctx->inner, 0x36, DGST_BLK_SZ);
  memset(ctx->outer, 0x5c, DGST_BLK_SZ);
   
  if (klen <= DGST_BLK_SZ) {
    for (i = 0;  i < klen;  i++) {
      ctx->inner[i] ^= key[i];
      ctx->outer[i] ^= key[i];
    }
  } else {
    DGST_Update(&(ctx->mdctx), key, klen);
    DGST_Final(dk, &(ctx->mdctx));
    DGST_Reset(&(ctx->mdctx));
    for (i = 0;  i < DGST_OUT_SZ;  i++) {
      ctx->inner[i] ^= dk[i];
      ctx->outer[i] ^= dk[i];
    }
  }
  DGST_Update(&(ctx->mdctx), ctx->inner, DGST_BLK_SZ);
}
   
void SPC_HMAC_Reset(SPC_HMAC_CTX *ctx) {
  DGST_Reset(&(ctx->mdctx));
  DGST_Update(&(ctx->mdctx), ctx->inner, DGST_BLK_SZ);
}
   
void SPC_HMAC_Update(SPC_HMAC_CTX *ctx, unsigned char *m, size_t l) {
  DGST_Update(&(ctx->mdctx), m, l);
}
   
void SPC_HMAC_Final(unsigned char *tag, SPC_HMAC_CTX *ctx) {
  unsigned char is[DGST_OUT_SZ];
   
  DGST_Final(is, &(ctx->mdctx));
  DGST_Reset(&(ctx->mdctx));
  DGST_Update(&(ctx->mdctx), ctx->outer, DGST_BLK_SZ);
  DGST_Update(&(ctx->mdctx), is, DGST_OUT_SZ);
  DGST_Final(tag, &(ctx->mdctx));
}
   
void SPC_HMAC_Cleanup(SPC_HMAC_CTX *ctx) {
  volatile char *p = ctx->inner;
  volatile char *q = ctx->outer;
  int i;
   
  for (i = 0;  i < DGST_BLK_SZ;  i++) *p++ = *q++ = 0;
}

The previous code does require a particular interface to a hash function interface. First, it requires two constants: DGST_BLK_SZ, which is the internal block size of the underlying hash function (see Recipe 6.3), and DGST_OUT_SZ, which is the size of the resulting message digest. Second, it requires a context type for the message digest, which you should typedef to DGST_CTX. Finally, it requires an incremental interface to the hash function:

void DGST_Init(DGST_CTX *ctx);
void DGST_Reset(DGST_CTX *ctx);
void DGST_Update(DGST_CTX *ctx, unsigned char *m, size_t len);
void DGST_Final(unsigned char *tag. DGST_CTX *ctx);

Some hash function implementations won't have an explicit reset implementation, in which case you can implement the reset functionality by calling DGST_Init( ) again.

Even though OpenSSL already has an HMAC implementation, here is an example of binding the previous HMAC implementation to OpenSSL's SHA1 implementation:

typedef SHA_CTX DGST_CTX;
#define DGST_BLK_SZ 64
#define DGST_OUT_SZ 20
   
#define DGST_Init(x)         SHA1_Init(x)
#define DGST_Reset(x)        DGST_Init(x)
#define DGST_Update(x, m, l) SHA1_Update(x, m, l)
#define DGST_Final(o, x)     SHA1_Final(o, x)

6.10.4 See Also

Recipe 5.26, Recipe 6.3, Recipe 6.4, Recipe 6.9

    [ Team LiB ] Previous Section Next Section