725d9af94a
The Babel MAC authentication RFC recommends implementing Blake2s as one of the supported algorithms. In order to achieve do this, add the blake2b and blake2s hash functions for MAC authentication. The hashing function implementations are the reference implementations from blake2.net. The Blake2 algorithms allow specifying an arbitrary output size, and the Babel MAC spec says to implement Blake2s with 128-bit output. To satisfy this, we add two different variants of each of the algorithms, one using the default size (256 bits for Blake2s, 512 bits for Blake2b), and one using half the default output size. Update to BIRD coding style done by committer.
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 \
|
|
}
|
|
|
|
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;
|
|
}
|