3 * The CMAC message-authentication code
5 * (c) 2017 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Catacomb.
12 * Catacomb is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
17 * Catacomb is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with Catacomb; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
28 #ifndef CATACOMB_CMAC_DEF_H
29 #define CATACOMB_CMAC_DEF_H
35 /*----- Header files ------------------------------------------------------*/
39 #include <mLib/bits.h>
42 #ifndef CATACOMB_ARENA_H
46 #ifndef CATACOMB_BLKC_H
50 #ifndef CATACOMB_GMAC_H
54 #ifndef CATACOMB_PARANOIA_H
55 # include "paranoia.h"
58 #ifndef CATACOMB_RSVR_H
62 /*----- Low-level OMAC definitions ----------------------------------------*/
64 /* --- @OMAC_BLOCK@ --- *
66 * Arguments: @PRE@, @pre@ = prefixes for the underlying block cipher
67 * @pre_ctx *k@ = pointer to expanded blockcipher key
68 * @uint32 *a@ = accumulator state to update
69 * @octet *p@ = pointer to input block to accumulate
71 * Use: Update the state @a@ from the input block @p@.
74 #define OMAC_BLOCK(PRE, pre, k, a, p) do { \
75 pre##_ctx *_k = (k); uint32 *_a = (a); const octet *_pp = (p); \
76 BLKC_XLOAD(PRE, _a, _pp); pre##_eblk(_k, _a, _a); \
79 /* --- @OMAC_DEF@ --- *
81 * Arguments: @PRE@, @pre@ = prefixes for the underlying block cipher
83 * Use: Creates low-level implementation for the OMAC message-
84 * authentication mode.
87 #define OMAC_DEF(PRE, pre) \
89 /* Buffering policy for OMAC. */ \
90 const rsvr_policy pre##_omacpolicy = \
91 { RSVRF_FULL, PRE##_BLKSZ, PRE##_BLKSZ }; \
93 /* --- @pre_omacmasks@ --- * \
95 * Arguments: @pre_ctx *k@ = pointer to expanded blockcipher key \
96 * @uint32 *m0, *m1@ = buffers to store the masks \
100 * Use: Initialize the OMAC masks. The mask buffers are \
101 * @PRE_BLKSZ/4@ words long. The mmask @m0@ is applied to \
102 * messages which are a whole number of blocks long; @m1@ \
103 * is applied to messages with an incomplete final block, \
104 * following the 10* padding. \
107 void pre##_omacmasks(pre##_ctx *k, uint32 *m0, uint32 *m1) \
109 uint32 t[PRE##_BLKSZ/4]; \
111 BLKC_ZERO(PRE, t); pre##_eblk(k, t, t); \
112 BLKC_BLSHIFT(PRE, IRRED, m0, t); \
113 BLKC_BLSHIFT(PRE, IRRED, m1, m0); \
116 /* --- @pre_omacdone@ --- * \
118 * Arguments: @pre_ctx *k@ = pointer to expanded blockcipher key \
119 * @const uint32 *m0, *m1@ = masks \
120 * @uint32 *a@ = accumulator state to update \
121 * @octet *p@ = pointer to input buffer (clobbered) \
122 * @unsigned n@ = size of input buffer (no more than \
127 * Use: Update and finalize the OMAC hash state with the last \
128 * few bytes of input. The final tag is left in @a@. \
131 void pre##_omacdone(pre##_ctx *k, const uint32 *m0, const uint32 *m1, \
132 uint32 *a, octet *p, unsigned n) \
134 /* Do any necessary padding and apply the appropriate mask to the \
137 if (n == PRE##_BLKSZ) \
138 BLKC_XMOVE(PRE, a, m0); \
140 p[n] = 0x80; memset(p + n + 1, 0, PRE##_BLKSZ - n - 1); \
141 BLKC_XMOVE(PRE, a, m1); \
144 /* Fetch the buffer contents and cycle the block cipher one last \
147 BLKC_XLOAD(PRE, a, p); \
148 pre##_eblk(k, a, a); \
151 /*----- Properly cooked CMAC ----------------------------------------------*/
153 /* --- @CMAC_DEF@ --- *
155 * Arguments: @PRE@, @pre@ = prefixes for the underlying block cipher
157 * Use: Creates an implementation for the CMAC message-authentication
161 #define CMAC_DEF(PRE, pre) CMAC_DEFX(PRE, pre, #pre, #pre)
163 #define CMAC_DEFX(PRE, pre, name, fname) \
167 /* --- @pre_cmacsetkey@ --- * \
169 * Arguments: @pre_cmackey *key@ = pointer to CMAC key block \
170 * @ocnst void *k@ = pointer to key material \
171 * @size_t ksz@ = size of key material \
175 * Use: Initializes a CMAC key. This can be used for several \
179 void pre##_cmacsetkey(pre##_cmackey *key, const void *k, size_t ksz) \
181 pre##_init(&key->ctx, k, ksz); \
182 pre##_omacmasks(&key->ctx, key->m0, key->m1); \
185 /* --- @pre##_cmacinit@ --- * \
187 * Arguments: @pre_cmacctx *ctx@ = pointer to context block \
188 * @pre_cmackey *k@ = key block \
192 * Use: Initializes a CMAC context ready to process a message. \
193 * It's not necessary to keep the key around. \
196 void pre##_cmacinit(pre##_cmacctx *ctx, const pre##_cmackey *k) \
197 { ctx->k = *k; ctx->off = 0; BLKC_ZERO(PRE, ctx->a); } \
199 /* --- @pre_cmachash@ --- * \
201 * Arguments: @pre_cmacctx *ctx@ = pointer to CMAC context block \
202 * @ocnst void *p@ = pointer to message buffer \
203 * @size_t sz@ = size of message buffer \
207 * Use: Hashes some input data. \
210 void pre##_cmachash(pre##_cmacctx *ctx, const void *p, size_t sz) \
215 rsvr_setup(&st, &pre##_omacpolicy, ctx->b, &ctx->off, p, sz); \
216 RSVR_DO(&st) while ((q = RSVR_NEXT(&st, PRE##_BLKSZ)) != 0) \
217 OMAC_BLOCK(PRE, pre, &ctx->k.ctx, ctx->a, q); \
220 /* --- @pre_cmacdone@ --- * \
222 * Arguments: @pre_cmacctx *ctx@ = pointer to CMAC context block \
223 * @void *t@ = where to write the tag \
227 * Use: Finishes a MAC operation and produces the tag. The \
228 * context is clobbered and can't be used for anything \
232 void pre##_cmacdone(pre##_cmacctx *ctx, void *t) \
234 pre##_omacdone(&ctx->k.ctx, ctx->k.m0, ctx->k.m1, \
235 ctx->a, ctx->b, ctx->off); \
236 BLKC_STORE(PRE, t, ctx->a); \
239 /* --- Generic MAC interface --- */ \
241 static const gmac_ops gkops; \
242 static const ghash_ops gops; \
244 typedef struct gkctx { \
249 typedef struct gctx { \
252 octet buf[PRE##_BLKSZ]; \
255 static ghash *gkinit(gmac *m) \
257 gkctx *gk = (gkctx *)m; \
258 gctx *g = S_CREATE(gctx); \
260 pre##_cmacinit(&g->c, &gk->k); \
264 static gmac *gkey(const void *k, size_t sz) \
266 gkctx *gk = S_CREATE(gkctx); \
267 gk->m.ops = &gkops; \
268 pre##_cmacsetkey(&gk->k, k, sz); \
272 static void ghhash(ghash *h, const void *p, size_t sz) \
273 { gctx *g = (gctx *)h; pre##_cmachash(&g->c, p, sz); } \
275 static octet *ghdone(ghash *h, void *buf) \
277 gctx *g = (gctx *)h; \
278 if (!buf) buf = g->buf; \
279 pre##_cmacdone(&g->c, buf); \
283 static ghash *ghcopy(ghash *h) \
285 gctx *g = (gctx *)h; \
286 gctx *gg = S_CREATE(gctx); \
287 memcpy(gg, g, sizeof(gctx)); \
291 static void ghdestroy(ghash *h) \
292 { gctx *g = (gctx *)h; BURN(*g); S_DESTROY(g); } \
294 static void gkdestroy(gmac *m) \
295 { gkctx *gk = (gkctx *)m; BURN(*gk); S_DESTROY(gk); } \
297 static ghash *ghinit(void) \
299 assert(((void)"Attempt to instantiate an unkeyed MAC", 0)); \
303 const gcmac pre##_cmac = \
304 { name "-cmac", PRE##_BLKSZ, pre##_keysz, gkey }; \
305 static const gmac_ops gkops = { &pre##_cmac, gkinit, gkdestroy }; \
306 static const gchash gch = { name "-cmac", PRE##_BLKSZ, ghinit }; \
307 static const ghash_ops gops = \
308 { &gch, ghhash, ghdone, ghdestroy, ghcopy }; \
310 CMAC_TESTX(PRE, pre, name, fname)
312 #define CMAC_TEST(PRE, pre) HMAC_TESTX(PRE, pre, #pre, #pre)
314 /* --- @CMAC_TEST@ --- *
316 * Arguments: @PRE@, @pre@ = prefixes for the underlying block cipher
318 * Use: Standard test rig for CMAC functions.
325 #include <mLib/dstr.h>
326 #include <mLib/macros.h>
327 #include <mLib/quis.h>
328 #include <mLib/testrig.h>
330 #define CMAC_TESTX(PRE, pre, name, fname) \
332 static int macverify(dstr *v) \
334 pre##_cmacctx cctx; \
335 pre##_cmackey ckey; \
339 int szs[] = { 1, 7, 192, -1, 0 }, *ip; \
344 dstr_ensure(&d, PRE##_BLKSZ); \
345 d.len = PRE##_BLKSZ; \
347 pre##_cmacsetkey(&ckey, v[0].buf, v[0].len); \
349 for (ip = szs; *ip; ip++) { \
356 p = (octet *)v[1].buf; \
357 pre##_cmacinit(&cctx, &ckey); \
361 pre##_cmachash(&cctx, p, i); \
365 pre##_cmacdone(&cctx, d.buf); \
366 if (MEMCMP(d.buf, !=, v[2].buf, PRE##_BLKSZ)) { \
367 printf("\nfail:\n\tstep = %i\n\tkey = ", *ip); \
368 type_hex.dump(&v[0], stdout); \
369 fputs("\n\tinput = ", stdout); \
370 type_hex.dump(&v[1], stdout); \
371 fputs("\n\texpected = ", stdout); \
372 type_hex.dump(&v[2], stdout); \
373 fputs("\n\tcomputed = ", stdout); \
374 type_hex.dump(&d, stdout); \
384 static test_chunk macdefs[] = { \
385 { name "-cmac", macverify, \
386 { &type_hex, &type_hex, &type_hex, 0 } }, \
390 int main(int argc, char *argv[]) \
393 test_run(argc, argv, macdefs, SRCDIR"/t/" fname); \
398 # define CMAC_TESTX(PRE, pre, name, fname)
401 /*----- That's all, folks -------------------------------------------------*/