Commit de2a27e2 authored by Ondrej Zajicek (work)'s avatar Ondrej Zajicek (work)
Browse files

Add generic message authentication interface

Add generic interface for generating and verifying MACs (message
authentication codes). Replace multiple HMAC implementation with
a generic one.
parent 7eec3988
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ H Library functions
S ip.c
S lists.c
S checksum.c bitops.c patmatch.c printf.c xmalloc.c tbf.c
S mac.c
D resource.sgml
S resource.c
S mempool.c
+2 −0
Original line number Diff line number Diff line
@@ -11,6 +11,8 @@ ip.h
ip.c
lists.c
lists.h
mac.c
mac.h
md5.c
md5.h
mempool.c

lib/mac.c

0 → 100644
+289 −0
Original line number Diff line number Diff line
/*
 *	BIRD Library -- Message Authentication Codes
 *
 *	(c) 2016 Ondrej Zajicek <santiago@crfreenet.org>
 *	(c) 2016 CZ.NIC z.s.p.o.
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

/**
 * DOC: Message authentication codes
 *
 * MAC algorithms are simple cryptographic tools for message authentication.
 * They use shared a secret key a and message text to generate authentication
 * code, which is then passed with the message to the other side, where the code
 * is verified. There are multiple families of MAC algorithms based on different
 * cryptographic primitives, BIRD implements two MAC families which use hash
 * functions.
 *
 * The first family is simply a cryptographic hash camouflaged as MAC algorithm.
 * Originally supposed to be (m|k)-hash (message is concatenated with key, and
 * that is hashed), but later it turned out that a raw hash is more practical.
 * This is used for cryptographic authentication in OSPFv2, RIP and BFD.
 *
 * The second family is the standard HMAC (RFC 2104), using inner and outer hash
 * to process key and message. HMAC (with SHA) is used in advanced OSPF and RIP
 * authentication (RFC 5709, RFC 4822).
 */

#include "lib/mac.h"
#include "lib/md5.h"
#include "lib/sha1.h"
#include "lib/sha256.h"
#include "lib/sha512.h"


/*
 *	Internal hash calls
 */

static inline void
hash_init(struct mac_context *mctx, struct hash_context *hctx)
{ mctx->type->hash_init(hctx); }

static inline void
hash_update(struct mac_context *mctx, struct hash_context *hctx, const byte *buf, uint len)
{ mctx->type->hash_update(hctx, buf, len); }

static inline byte *
hash_final(struct mac_context *mctx, struct hash_context *hctx)
{ return mctx->type->hash_final(hctx); }

static inline void
hash_buffer(struct mac_context *mctx, byte *outbuf, const byte *buffer, uint length)
{
  struct hash_context hctx;

  hash_init(mctx, &hctx);
  hash_update(mctx, &hctx, buffer, length);
  memcpy(outbuf, hash_final(mctx, &hctx), mctx->type->hash_size);
}


/*
 *	(not-really-MAC) Hash
 */

static void
nrmh_init(struct mac_context *ctx, const byte *key UNUSED, uint keylen UNUSED)
{
  struct nrmh_context *ct = (void *) ctx;
  hash_init(ctx, &ct->ictx);
}

static void
nrmh_update(struct mac_context *ctx, const byte *data, uint datalen)
{
  struct nrmh_context *ct = (void *) ctx;
  hash_update(ctx, &ct->ictx, data, datalen);
}

static byte *
nrmh_final(struct mac_context *ctx)
{
  struct nrmh_context *ct = (void *) ctx;
  return hash_final(ctx, &ct->ictx);
}


/*
 *	HMAC
 */

static void
hmac_init(struct mac_context *ctx, const byte *key, uint keylen)
{
  struct hmac_context *ct = (void *) ctx;
  uint block_size = ctx->type->block_size;
  uint hash_size = ctx->type->hash_size;

  byte *keybuf = alloca(block_size);
  byte *buf = alloca(block_size);
  uint i;

  /* Hash the key if necessary */
  if (keylen <= block_size)
  {
    memcpy(keybuf, key, keylen);
    memset(keybuf + keylen, 0, block_size - keylen);
  }
  else
  {
    hash_buffer(ctx, keybuf, key, keylen);
    memset(keybuf + hash_size, 0, block_size - hash_size);
  }

  /* Initialize the inner digest */
  hash_init(ctx, &ct->ictx);
  for (i = 0; i < block_size; i++)
    buf[i] = keybuf[i] ^ 0x36;
  hash_update(ctx, &ct->ictx, buf, block_size);

  /* Initialize the outer digest */
  hash_init(ctx, &ct->octx);
  for (i = 0; i < block_size; i++)
    buf[i] = keybuf[i] ^ 0x5c;
  hash_update(ctx, &ct->octx, buf, block_size);
}

static void
hmac_update(struct mac_context *ctx, const byte *data, uint datalen)
{
  struct hmac_context *ct = (void *) ctx;

  /* Just update the inner digest */
  hash_update(ctx, &ct->ictx, data, datalen);
}

static byte *
hmac_final(struct mac_context *ctx)
{
  struct hmac_context *ct = (void *) ctx;

  /* Finish the inner digest */
  byte *isha = hash_final(ctx, &ct->ictx);

  /* Finish the outer digest */
  hash_update(ctx, &ct->octx, isha, ctx->type->hash_size);
  return hash_final(ctx, &ct->octx);
}


/*
 *	Common code
 */

#define HASH_DESC(name, px, PX) \
  { name, PX##_SIZE, sizeof(struct nrmh_context), nrmh_init, nrmh_update, nrmh_final, \
    PX##_SIZE, PX##_BLOCK_SIZE, px##_init, px##_update, px##_final }

#define HMAC_DESC(name, px, PX)						\
  { name, PX##_SIZE, sizeof(struct hmac_context), hmac_init, hmac_update, hmac_final, \
    PX##_SIZE, PX##_BLOCK_SIZE, px##_init, px##_update, px##_final }

const struct mac_desc mac_table[ALG_MAX] = {
  [ALG_MD5] =		HASH_DESC("Keyed MD5",		md5,	MD5),
  [ALG_SHA1] =		HASH_DESC("Keyed SHA-1",	sha1,	SHA1),
  [ALG_SHA224] =	HASH_DESC("Keyed SHA-224",	sha224,	SHA224),
  [ALG_SHA256] = 	HASH_DESC("Keyed SHA-256",	sha256,	SHA256),
  [ALG_SHA384] = 	HASH_DESC("Keyed SHA-384",	sha384,	SHA384),
  [ALG_SHA512] = 	HASH_DESC("Keyed SHA-512",	sha512,	SHA512),
  [ALG_HMAC_MD5] = 	HMAC_DESC("HMAC-MD5",		md5,	MD5),
  [ALG_HMAC_SHA1] = 	HMAC_DESC("HMAC-SHA-1",		sha1,	SHA1),
  [ALG_HMAC_SHA224] = 	HMAC_DESC("HMAC-SHA-224",	sha224,	SHA224),
  [ALG_HMAC_SHA256] = 	HMAC_DESC("HMAC-SHA-256",	sha256,	SHA256),
  [ALG_HMAC_SHA384] = 	HMAC_DESC("HMAC-SHA-384",	sha384,	SHA384),
  [ALG_HMAC_SHA512] = 	HMAC_DESC("HMAC-SHA-512",	sha512,	SHA512),
};


/**
 * mac_init - initialize MAC algorithm
 * @ctx: context to initialize
 * @id: MAC algorithm ID
 * @key: MAC key
 * @keylen: MAC key length
 *
 * Initialize MAC context @ctx for algorithm @id (e.g., %ALG_HMAC_SHA1), with
 * key @key of length @keylen. After that, message data could be added using
 * mac_update() function.
 */
void
mac_init(struct mac_context *ctx, uint id, const byte *key, uint keylen)
{
  ctx->type = &mac_table[id];
  ctx->type->init(ctx, key, keylen);
}

#if 0
/**
 * mac_update - add more data to MAC algorithm
 * @ctx: MAC context
 * @data: data to add
 * @datalen: length of data
 *
 * Push another @datalen bytes of data pointed to by @data into the MAC
 * algorithm currently in @ctx. Can be called multiple times for the same MAC
 * context. It has the same effect as concatenating all the data together and
 * passing them at once.
 */
void mac_update(struct mac_context *ctx, const byte *data, uint datalen)
{ DUMMY; }

/**
 * mac_final - finalize MAC algorithm
 * @ctx: MAC context
 *
 * Finish MAC computation and return a pointer to the result. No more
 * @mac_update() calls could be done, but the context may be reinitialized
 * later.
 *
 * Note that the returned pointer points into data in the @ctx context. If it
 * ceases to exist, the pointer becomes invalid.
 */
byte *mac_final(struct mac_context *ctx)
{ DUMMY; }

/**
 * mac_cleanup - cleanup MAC context
 * @ctx: MAC context
 *
 * Cleanup MAC context after computation (by filling with zeros). Not strictly
 * necessary, just to erase sensitive data from stack. This also invalidates the
 * pointer returned by @mac_final().
 */
void mac_cleanup(struct mac_context *ctx)
{ DUMMY; }

#endif

/**
 * mac_fill - compute and fill MAC
 * @id: MAC algorithm ID
 * @key: secret key
 * @keylen: key length
 * @data: message data
 * @datalen: message length
 * @mac: place to fill MAC
 *
 * Compute MAC for specified key @key and message @data using algorithm @id and
 * copy it to buffer @mac. mac_fill() is a shortcut function doing all usual
 * steps for transmitted messages.
 */
void
mac_fill(uint id, const byte *key, uint keylen, const byte *data, uint datalen, byte *mac)
{
  struct mac_context ctx;

  mac_init(&ctx, id, key, keylen);
  mac_update(&ctx, data, datalen);
  memcpy(mac, mac_final(&ctx), mac_get_length(&ctx));
  mac_cleanup(&ctx);
}

/**
 * mac_verify - compute and verify MAC
 * @id: MAC algorithm ID
 * @key: secret key
 * @keylen: key length
 * @data: message data
 * @datalen: message length
 * @mac: received MAC
 *
 * Compute MAC for specified key @key and message @data using algorithm @id and
 * compare it with received @mac, return whether they are the same. mac_verify()
 * is a shortcut function doing all usual steps for received messages.
 */
int
mac_verify(uint id, const byte *key, uint keylen, const byte *data, uint datalen, const byte *mac)
{
  struct mac_context ctx;

  mac_init(&ctx, id, key, keylen);
  mac_update(&ctx, data, datalen);
  int res = !memcmp(mac, mac_final(&ctx), mac_get_length(&ctx));
  mac_cleanup(&ctx);

  return res;
}

lib/mac.h

0 → 100644
+117 −0
Original line number Diff line number Diff line
/*
 *	BIRD Library -- Message Authentication Codes
 *
 *	(c) 2016 Ondrej Zajicek <santiago@crfreenet.org>
 *	(c) 2016 CZ.NIC z.s.p.o.
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

#ifndef _BIRD_MAC_H_
#define _BIRD_MAC_H_

#include "nest/bird.h"
#include "lib/sha512.h"


#define ALG_UNDEFINED		0
#define ALG_MD5			0x01
#define ALG_SHA1		0x02
#define ALG_SHA224		0x03
#define ALG_SHA256		0x04
#define ALG_SHA384		0x05
#define ALG_SHA512		0x06
#define ALG_HMAC_MD5		0x11
#define ALG_HMAC_SHA1		0x12
#define ALG_HMAC_SHA224		0x13
#define ALG_HMAC_SHA256		0x14
#define ALG_HMAC_SHA384		0x15
#define ALG_HMAC_SHA512		0x16
#define ALG_MAX			0x17

/* These are maximums for HASH/MAC lengths and required context space */
#define MAX_HASH_SIZE		SHA512_SIZE
#define HASH_STORAGE		sizeof(struct sha512_context)
#define MAC_STORAGE		sizeof(struct hmac_context)

/* Generic context used by hash functions */
struct hash_context
{
  u8 data[HASH_STORAGE];
  u64 align[0];
};

/* Context for embedded hash (not-really-MAC hash) */
struct nrmh_context {
  const struct mac_desc *type;
  struct hash_context ictx;
};

/* Context for hash based HMAC */
struct hmac_context {
  const struct mac_desc *type;
  struct hash_context ictx;
  struct hash_context octx;
};

/* Generic context used by MAC functions */
struct mac_context
{
  const struct mac_desc *type;
  u8 data[MAC_STORAGE - sizeof(void *)];
  u64 align[0];
};

/* Union to satisfy C aliasing rules */
union mac_context_union {
  struct mac_context mac;
  struct nrmh_context nrmh;
  struct hmac_context hmac;
};


struct mac_desc {
  const char *name;			/* Name of MAC algorithm */
  uint mac_length;			/* Length of authentication code */
  uint ctx_length;			/* Length of algorithm context */
  void (*init)(struct mac_context *ctx, const byte *key, uint keylen);
  void (*update)(struct mac_context *ctx, const byte *data, uint datalen);
  byte *(*final)(struct mac_context *ctx);

  uint hash_size;			/* Hash length, for hash-based MACs */
  uint block_size;			/* Hash block size, for hash-based MACs */
  void (*hash_init)(struct hash_context *ctx);
  void (*hash_update)(struct hash_context *ctx, const byte *data, uint datalen);
  byte *(*hash_final)(struct hash_context *ctx);
};

const struct mac_desc mac_table[ALG_MAX];

static inline const char *mac_type_name(uint id)
{ return mac_table[id].name; }

static inline uint mac_type_length(uint id)
{ return mac_table[id].mac_length; }

static inline const char *mac_get_name(struct mac_context *ctx)
{ return ctx->type->name; }

static inline uint mac_get_length(struct mac_context *ctx)
{ return ctx->type->mac_length; }

void mac_init(struct mac_context *ctx, uint id, const byte *key, uint keylen);

static inline void mac_update(struct mac_context *ctx, const byte *data, uint datalen)
{ ctx->type->update(ctx, data, datalen); }

static inline byte *mac_final(struct mac_context *ctx)
{ return ctx->type->final(ctx); }

static inline void mac_cleanup(struct mac_context *ctx)
{ memset(ctx, 0, ctx->type->ctx_length); }

void mac_fill(uint id, const byte *key, uint keylen, const byte *data, uint datalen, byte *mac);
int mac_verify(uint id, const byte *key, uint keylen, const byte *data, uint datalen, const byte *mac);


#endif /* _BIRD_MAC_H_ */
+7 −74
Original line number Diff line number Diff line
@@ -39,8 +39,10 @@ static void md5_transform(u32 buf[4], u32 const in[16]);
 * initialization constants.
 */
void
md5_init(struct md5_context *ctx)
md5_init(struct hash_context *CTX)
{
  struct md5_context *ctx = (void *) CTX;

  ctx->buf[0] = 0x67452301;
  ctx->buf[1] = 0xefcdab89;
  ctx->buf[2] = 0x98badcfe;
@@ -55,8 +57,9 @@ md5_init(struct md5_context *ctx)
 * of bytes.
 */
void
md5_update(struct md5_context *ctx, const byte *buf, uint len)
md5_update(struct hash_context *CTX, const byte *buf, uint len)
{
  struct md5_context *ctx = (void *) CTX;
  u32 t;

  /* Update bitcount */
@@ -105,8 +108,9 @@ md5_update(struct md5_context *ctx, const byte *buf, uint len)
 * 1 0* (64-bit count of bits processed, MSB-first)
 */
byte *
md5_final(struct md5_context *ctx)
md5_final(struct hash_context *CTX)
{
  struct md5_context *ctx = (void *) CTX;
  uint count;
  byte *p;

@@ -149,13 +153,6 @@ md5_final(struct md5_context *ctx)
  return (byte*) ctx->buf;
}

/* I am a hard paranoid */
void
md5_erase_ctx(struct md5_context *ctx)
{
  memset((char *) ctx, 0, sizeof(*ctx));	/* In case it's sensitive */
}

/* The four core functions - F1 is optimized somewhat */

/* #define F1(x, y, z) (x & y | ~x & z) */
@@ -256,67 +253,3 @@ md5_transform(u32 buf[4], u32 const in[16])
  buf[2] += c;
  buf[3] += d;
}


/*
 * 	MD5-HMAC
 */

static void
md5_hash_buffer(byte *outbuf, const byte *buffer, size_t length)
{
  struct md5_context hd_tmp;

  md5_init(&hd_tmp);
  md5_update(&hd_tmp, buffer, length);
  memcpy(outbuf, md5_final(&hd_tmp), MD5_SIZE);
}

void
md5_hmac_init(struct md5_hmac_context *ctx, const byte *key, size_t keylen)
{
  byte keybuf[MD5_BLOCK_SIZE], buf[MD5_BLOCK_SIZE];

  /* Hash the key if necessary */
  if (keylen <= MD5_BLOCK_SIZE)
  {
    memcpy(keybuf, key, keylen);
    bzero(keybuf + keylen, MD5_BLOCK_SIZE - keylen);
  }
  else
  {
    md5_hash_buffer(keybuf, key, keylen);
    bzero(keybuf + MD5_SIZE, MD5_BLOCK_SIZE - MD5_SIZE);
  }

  /* Initialize the inner digest */
  md5_init(&ctx->ictx);
  int i;
  for (i = 0; i < MD5_BLOCK_SIZE; i++)
    buf[i] = keybuf[i] ^ 0x36;
  md5_update(&ctx->ictx, buf, MD5_BLOCK_SIZE);

  /* Initialize the outer digest */
  md5_init(&ctx->octx);
  for (i = 0; i < MD5_BLOCK_SIZE; i++)
    buf[i] = keybuf[i] ^ 0x5c;
  md5_update(&ctx->octx, buf, MD5_BLOCK_SIZE);
}

void
md5_hmac_update(struct md5_hmac_context *ctx, const byte *buf, size_t buflen)
{
  /* Just update the inner digest */
  md5_update(&ctx->ictx, buf, buflen);
}

byte *
md5_hmac_final(struct md5_hmac_context *ctx)
{
  /* Finish the inner digest */
  byte *isha = md5_final(&ctx->ictx);

  /* Finish the outer digest */
  md5_update(&ctx->octx, isha, MD5_SIZE);
  return md5_final(&ctx->octx);
}
Loading