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;
 | |
| }
 |