589f7d1e4f
Add min/max key length fields to the MAC algorithm description and validate configured keys before they are used.
307 lines
8.6 KiB
C
307 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;
|
|
}
|