aboutsummaryrefslogtreecommitdiffstats
path: root/lib/mac.c
blob: f07d38dffdb318407423f074aebb54358bdd2483 (plain)
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
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/*
 *	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;
}