mac.c 8.04 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
/*
 *	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;
}