mirror of
				https://gitlab.labs.nic.cz/labs/bird.git
				synced 2024-05-11 16:54:54 +00:00 
			
		
		
		
	Add min/max key length fields to the MAC algorithm description and validate configured keys before they are used.
		
			
				
	
	
		
			308 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			308 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 *	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"
 | 
						|
#include "lib/blake2.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	\
 | 
						|
  }
 | 
						|
 | 
						|
#define BLAKE_DESC(name, vx, VX, size)					\
 | 
						|
  {									\
 | 
						|
    name, size/8, sizeof(struct vx##_context),				\
 | 
						|
    vx##_mac_init, vx##_mac_update, vx##_mac_final,			\
 | 
						|
    size/8, VX##_BLOCK_SIZE, NULL, NULL, NULL, 0, VX##_SIZE		\
 | 
						|
  }
 | 
						|
 | 
						|
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_BLAKE2S_128] = 	BLAKE_DESC("Blake2s-128",	blake2s, BLAKE2S, 128),
 | 
						|
  [ALG_BLAKE2S_256] = 	BLAKE_DESC("Blake2s-256",	blake2s, BLAKE2S, 256),
 | 
						|
  [ALG_BLAKE2B_256] = 	BLAKE_DESC("Blake2b-256",	blake2b, BLAKE2B, 256),
 | 
						|
  [ALG_BLAKE2B_512] = 	BLAKE_DESC("Blake2b-512",	blake2b, BLAKE2B, 512),
 | 
						|
  [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;
 | 
						|
}
 |