3 * The CCM authenticated-encryption mode
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_CCM_DEF_H
29 #define CATACOMB_CCM_DEF_H
35 /*----- Header files ------------------------------------------------------*/
39 #include <mLib/bits.h>
42 #ifndef CATACOMB_ARENA_H
46 #ifndef CATACOMB_BLKC_H
54 #ifndef CATACOMB_KEYSZ_H
58 #ifndef CATACOMB_PARANOIA_H
59 # include "paranoia.h"
62 #ifndef CATACOMB_RSVR_H
66 /*----- Common machinery --------------------------------------------------*/
68 /* --- @ccm_check@ --- *
70 * Arguments: @const ccm_params *p@ = pointer to parameters
72 * Returns: True (nonzero) if the parameters are OK; false (zero) if
75 * Use: Verify that the CCM parameters are acceptable.
78 extern int ccm_check(const ccm_params */*p*/);
80 /* --- @ccm_fmthdr@ --- *
82 * Arguments: @const ccm_params *p@ = pointer to parameters
83 * @octet *b@ = block-size buffer to write header
84 * @const void *n@ = pointer to nonce
88 * Use: Format a MAC header block.
91 extern void ccm_fmthdr(const ccm_params */*p*/,
92 octet */*b*/, const void */*n*/);
94 /* --- @ccm_fmtctr@ --- *
96 * Arguments: @const ccm_params *p@ = pointer to parameters
97 * @octet *b@ = block-size buffer to write header
98 * @const void *n@ = pointer to nonce
102 * Use: Format an initial counter block.
105 extern void ccm_fmtctr(const ccm_params */*p*/,
106 octet */*b*/, const void */*n*/);
108 /*----- Macros ------------------------------------------------------------*/
110 /* --- @CCM_DEF@ --- *
112 * Arguments: @PRE@, @pre@ = prefixes for the underlying block cipher
114 * Use: Creates an implementation for the CCM authenticated-
118 #define CCM_DEF(PRE, pre) CCM_DEFX(PRE, pre, #pre, #pre)
120 #define CCM_DEFX(PRE, pre, name, fname) \
122 const octet pre##_ccmnoncesz[] = \
123 { KSZ_RANGE, PRE##_BLKSZ/2 - (PRE##_BLKSZ <= 16 ? 1 : 2), \
124 CCM_NSZMIN(PRE), CCM_NSZMAX(PRE), 1 }; \
125 const octet pre##_ccmtagsz[] = \
126 { KSZ_RANGE, CCM_TSZMAX(PRE), \
127 CCM_TSZMIN(PRE), CCM_TSZMAX(PRE), PRE##_BLKSZ == 16 ? 2 : 1 }; \
129 static const rsvr_policy pre##_ccmpolicy = \
130 { RSVRF_FULL, PRE##_BLKSZ, PRE##_BLKSZ }; \
132 /* --- @pre_ccminthash@ --- * \
134 * Arguments: @pre_ccmctx *ctx@ = pointer to context block \
135 * @const void *p@ = pointer to material to hash \
136 * @size_t sz@ = size of the input buffer \
140 * Use: Internal operation for feeding stuff into the CBC-MAC \
144 static void pre##_ccminthash(pre##_ccmctx *ctx, \
145 const void *p, size_t sz) \
150 rsvr_setup(&st, &pre##_ccmpolicy, ctx->b, &ctx->off, p, sz); \
151 RSVR_DO(&st) while ((q = RSVR_NEXT(&st, PRE##_BLKSZ)) != 0) { \
152 BLKC_XLOAD(PRE, ctx->a, q); \
153 pre##_eblk(&ctx->k, ctx->a, ctx->a); \
157 /* --- @pre_ccminit@ --- * \
159 * Arguments: @pre_ccmctx *aad@ = pointer to CCM context \
160 * @const pre_ctx *k@ = pointer to key material \
161 * @const void *n@ = pointer to nonce \
162 * @size_t nsz@ = size of the nonce \
163 * @size_t hsz@ = size of the AAD \
164 * @size_t msz@ = size of the message/ciphertext \
165 * @size_t tsz@ = size of the tag to produce \
167 * Returns: Zero on success; nonzero if the parameters are invalid. \
169 * Use: Initialize an CCM operation context with a given key. \
171 * The original key needn't be kept around any more. \
174 int pre##_ccminit(pre##_ccmctx *ctx, const pre##_ctx *k, \
175 const void *n, size_t nsz, \
176 size_t hsz, size_t msz, size_t tsz) \
177 { ctx->k = *k; return (pre##_ccmreinit(ctx, n, nsz, hsz, msz, tsz)); } \
179 /* --- @pre_ccmreinit@ --- * \
181 * Arguments: @pre_ccmctx *ctx@ = pointer to CCM context \
182 * @const void *n@ = pointer to nonce \
183 * @size_t nsz@ = size of nonce \
184 * @size_t hsz@ = size of the AAD \
185 * @size_t msz@ = size of the message/ciphertext \
186 * @size_t tsz@ = size of the tag to produce \
188 * Returns: Zero on success; nonzero if the parameters are invalid. \
190 * Use: Reinitialize an CCM operation context, changing the \
194 int pre##_ccmreinit(pre##_ccmctx *ctx, const void *n, size_t nsz, \
195 size_t hsz, size_t msz, size_t tsz) \
201 /* Set up the parameters and check that they make sense. */ \
202 ctx->p.hsz = hsz; ctx->p.msz = msz; \
203 ctx->p.bsz = PRE##_BLKSZ; ctx->p.nsz = nsz; ctx->p.tsz = tsz; \
204 if (!ccm_check(&ctx->p)) return (-1); \
206 /* Prepare the counter and the final MAC mask. The initial counter \
207 * is used to make the MAC mask, so generate that, keeping it for \
210 ccm_fmtctr(&ctx->p, ctx->b, n); \
211 BLKC_LOAD(PRE, ctx->c, ctx->b); \
212 pre##_eblk(&ctx->k, ctx->c, ctx->s0); \
214 /* Prepare the MAC header and leave it in the buffer. */ \
215 ccm_fmthdr(&ctx->p, ctx->b, n); \
216 BLKC_ZERO(PRE, ctx->a); \
218 /* Initialize our state. The buffer is currently full (with the \
219 * MAC header), and we're always awaiting AAD, though we've not yet \
220 * seen any. (Even if we're not expecting AAD, this will trigger \
221 * appropriate initialization when encryption or decryption begins.) \
223 ctx->off = PRE##_BLKSZ; ctx->i = 0; \
224 ctx->st = CCMST_AAD; \
226 /* If there's AAD to come, then do the AAD framing. This aligns \
227 * badly with the blocking, so feed the framing in the hard way. \
231 { STORE16(b, hsz); sz = 2; } \
232 else if (hsz <= MASK32) \
233 { b[0] = 0xff; b[1] = 0xfe; STORE32(b + 2, hsz); sz = 6; } \
235 b[0] = b[1] = 0xff; \
236 ASSIGN64(t, hsz); STORE64_(b + 2, t); \
239 pre##_ccminthash(ctx, b, sz); \
246 /* --- @pre_ccmaadhash@ --- * \
248 * Arguments: @pre_ccmctx *ctx@ = pointer to AAD context \
249 * @const void *p@ = pointer to AAD material \
250 * @size_t sz@ = length of AAD material \
254 * Use: Feeds AAD into the context. This must be done before \
255 * any of the message/ciphertext is processed because CCM \
256 * is really annoying like that. \
259 void pre##_ccmaadhash(pre##_ccmctx *ctx, const void *p, size_t sz) \
261 assert(ctx->st == CCMST_AAD); \
262 assert(sz <= ctx->p.hsz - ctx->i); \
264 pre##_ccminthash(ctx, p, sz); \
267 /* --- @pre_ccmencdecsetup@ --- * \
269 * Arguments: @pre_ccmctx *ctx@ = pointer to context block \
270 * @size_t sz@ = size of message block \
274 * Use: Prepares for an encrypt or decryption operation, \
275 * transitioning from the AAD state and updating the \
279 static void pre##_ccmencdecsetup(pre##_ccmctx *ctx, size_t sz) \
281 if (ctx->st != CCMST_MSG) { \
282 /* Make sure we're currently in the AAD state and we've seen all of \
283 * the AAD we expected. \
285 assert(ctx->st == CCMST_AAD); \
286 assert(ctx->i == ctx->p.hsz); \
288 /* Pad the final AAD block out until we hit a block boundary. Note \
289 * that we don't cycle the block cipher here: instead, leave the \
290 * buffer full so that we do that next time. \
292 memset(ctx->b + ctx->off, 0, PRE##_BLKSZ - ctx->off); \
293 ctx->off = PRE##_BLKSZ; \
295 /* Now we're ready to process the message text. */ \
296 ctx->st = CCMST_MSG; ctx->i = 0; \
299 /* Update the size. */ \
300 assert(sz <= ctx->p.msz - ctx->i); \
304 /* --- @pre_ccmencrypt@ --- * \
306 * Arguments: @pre_ccmctx *ctx@ = pointer to CCM operation context \
307 * @const void *src@ = pointer to plaintext message chunk \
308 * @size_t sz@ = size of the plaintext \
309 * @buf *dst@ = a buffer to write the ciphertext to \
311 * Returns: Zero on success; @-1@ on failure. \
313 * Use: Encrypts a chunk of a plaintext message, writing a \
314 * chunk of ciphertext to the output buffer and updating \
315 * the operation state. \
317 * For CCM, we always write a ciphertext chunk the same \
318 * size as the plaintext. The messing about with @buf@ \
319 * objects makes the interface consistent with other AEAD \
320 * schemes which can't do this. \
323 int pre##_ccmencrypt(pre##_ccmctx *ctx, \
324 const void *src, size_t sz, buf *dst) \
327 uint32 t[PRE##_BLKSZ/4], u[PRE##_BLKSZ]; \
328 const octet *p = src; \
331 /* Allocate space for the ciphertext. */ \
332 if (sz) { q = buf_get(dst, sz); if (!q) return (-1); } \
335 /* Set stuff up. */ \
336 pre##_ccmencdecsetup(ctx, sz); \
338 /* Determine the buffering plan. Our buffer is going to do double- \
339 * duty here. The end portion is going to contain mask from the \
340 * encrypted counter which we mix into the plaintext to encrypt it; \
341 * the start portion, which originally contained mask bytes we've \
342 * already used, will hold the input plaintext, which will \
343 * eventually be collected into the CBC-MAC state. \
345 rsvr_mkplan(&plan, &pre##_ccmpolicy, ctx->off, sz); \
347 /* Initial portion, fulfilled from the buffer. If the buffer is \
348 * empty, then that means that we haven't yet encrypted the current \
349 * counter, so we should do that and advance it. \
353 BLKC_BSTEP(PRE, ctx->c); pre##_eblk(&ctx->k, ctx->c, t); \
354 BLKC_STORE(PRE, ctx->b, t); \
356 r = ctx->b + ctx->off; ctx->off += plan.head; \
357 while (plan.head--) { y = *p++; *q++ = y ^ *r; *r++ = y; } \
360 /* If we've filled up the buffer then we need to cycle the MAC and \
361 * reset the offset. \
363 if (plan.from_rsvr) { \
364 BLKC_XLOAD(PRE, ctx->a, ctx->b); \
365 pre##_eblk(&ctx->k, ctx->a, ctx->a); \
369 /* Now to process the main body of the input. */ \
370 while (plan.from_input) { \
371 BLKC_BSTEP(PRE, ctx->c); pre##_eblk(&ctx->k, ctx->c, t); \
372 BLKC_LOAD(PRE, u, p); p += PRE##_BLKSZ; \
373 BLKC_XSTORE(PRE, q, t, u); q += PRE##_BLKSZ; \
374 BLKC_XMOVE(PRE, ctx->a, u); pre##_eblk(&ctx->k, ctx->a, ctx->a); \
375 plan.from_input -= PRE##_BLKSZ; \
378 /* Finally, deal with any final portion. If there is one, we know \
379 * that the buffer is empty: we must have filled it above, or this \
380 * would all count as `initial' data. \
383 BLKC_BSTEP(PRE, ctx->c); pre##_eblk(&ctx->k, ctx->c, t); \
384 BLKC_STORE(PRE, ctx->b, t); \
385 r = ctx->b; ctx->off = plan.tail; \
386 while (plan.tail--) { y = *p++; *q++ = y ^ *r; *r++ = y; } \
393 /* --- @pre_ccmdecrypt@ --- * \
395 * Arguments: @pre_ccmctx *ctx@ = pointer to CCM operation context \
396 * @const void *src@ = pointer to ciphertext message chunk \
397 * @size_t sz@ = size of the ciphertext \
398 * @buf *dst@ = a buffer to write the plaintext to \
400 * Returns: Zero on success; @-1@ on failure. \
402 * Use: Decrypts a chunk of a ciphertext message, writing a \
403 * chunk of plaintext to the output buffer and updating \
404 * the operation state. \
406 * For CCM, we always write a plaintext chunk the same \
407 * size as the ciphertext. The messing about with @buf@ \
408 * objects makes the interface consistent with other AEAD \
409 * schemes which can't do this. \
412 int pre##_ccmdecrypt(pre##_ccmctx *ctx, \
413 const void *src, size_t sz, buf *dst) \
416 uint32 t[PRE##_BLKSZ/4]; \
417 const octet *p = src; \
420 /* Allocate space for the plaintext. */ \
421 if (sz) { q = buf_get(dst, sz); if (!q) return (-1); } \
424 /* Set stuff up. */ \
425 pre##_ccmencdecsetup(ctx, sz); \
427 /* Determine the buffering plan. Our buffer is going to do double- \
428 * duty here. The end portion is going to contain mask from the \
429 * encrypted counter which we mix into the plaintext to encrypt it; \
430 * the start portion, which originally mask contained bytes we've \
431 * already used, will hold the recovered plaintext, which will \
432 * eventually be collected into the CBC-MAC state. \
434 rsvr_mkplan(&plan, &pre##_ccmpolicy, ctx->off, sz); \
436 /* Initial portion, fulfilled from the buffer. If the buffer is \
437 * empty, then that means that we haven't yet encrypted the current \
438 * counter, so we should do that and advance it. \
442 BLKC_BSTEP(PRE, ctx->c); pre##_eblk(&ctx->k, ctx->c, t); \
443 BLKC_STORE(PRE, ctx->b, t); \
445 r = ctx->b + ctx->off; ctx->off += plan.head; \
446 while (plan.head--) { y = *p++ ^ *r; *q++ = *r++ = y; } \
449 /* If we've filled up the buffer then we need to cycle the MAC and \
450 * reset the offset. \
452 if (plan.from_rsvr) { \
453 BLKC_XLOAD(PRE, ctx->a, ctx->b); \
454 pre##_eblk(&ctx->k, ctx->a, ctx->a); \
458 /* Now to process the main body of the input. */ \
459 while (plan.from_input) { \
460 BLKC_BSTEP(PRE, ctx->c); pre##_eblk(&ctx->k, ctx->c, t); \
461 BLKC_XLOAD(PRE, t, p); p += PRE##_BLKSZ; \
462 BLKC_STORE(PRE, q, t); q += PRE##_BLKSZ; \
463 BLKC_XMOVE(PRE, ctx->a, t); pre##_eblk(&ctx->k, ctx->a, ctx->a); \
464 plan.from_input -= PRE##_BLKSZ; \
467 /* Finally, deal with any final portion. If there is one, we know \
468 * that the buffer is empty: we must have filled it above, or this \
469 * would all count as `initial' data. \
472 BLKC_BSTEP(PRE, ctx->c); pre##_eblk(&ctx->k, ctx->c, t); \
473 BLKC_STORE(PRE, ctx->b, t); \
474 r = ctx->b; ctx->off = plan.tail; \
475 while (plan.tail--) { y = *p++ ^ *r; *q++ = *r++ = y; } \
482 /* --- @pre_ccmtag@ --- * \
484 * Arguments: @pre_ccmctx *ctx@ = pointer to an CCM context \
485 * @octet *t@ = where to write a (full-length) tag \
486 * @size_t tsz@ = size of the tag (to check) \
490 * Use: Finishes an CCM operation, by calculating the tag. \
493 static void pre##_ccmtag(pre##_ccmctx *ctx, octet *t, size_t tsz) \
495 /* Make sure we're in good shape. It's just about possible that \
496 * we're still in the AAD state, but there was no actual message, so \
497 * handle this situation. \
501 assert(ctx->i == ctx->p.hsz); \
502 assert(!ctx->p.msz); \
505 /* hsz already checked in `pre_ccmencdecsetup'. */ \
506 assert(ctx->i == ctx->p.msz); \
510 assert(tsz == ctx->p.tsz); \
512 /* Pad the final plaintext block out and cycle the block cipher one \
515 memset(ctx->b + ctx->off, 0, PRE##_BLKSZ - ctx->off); \
516 BLKC_XLOAD(PRE, ctx->a, ctx->b); \
517 pre##_eblk(&ctx->k, ctx->a, ctx->a); \
519 /* Mask the CBC-MAC tag (which prevents the standard extension \
520 * attack) and store the result. \
522 BLKC_XSTORE(PRE, t, ctx->a, ctx->s0); \
525 /* --- @pre_ccmencryptdone@ --- * \
527 * Arguments: @pre_ccmctx *ctx@ = pointer to an CCM context \
528 * @buf *dst@ = buffer for remaining ciphertext \
529 * @void *tag@ = where to write the tag \
530 * @size_t tsz@ = length of tag to store \
532 * Returns: Zero on success; @-1@ on failure. \
534 * Use: Completes an CCM encryption operation. The @aad@ \
535 * pointer may be null if there is no additional \
536 * authenticated data. CCM doesn't buffer ciphertext, but \
537 * the output buffer is provided anyway for consistency \
538 * with other AEAD schemes which don't have this property; \
539 * the function will fail if the output buffer is broken. \
542 int pre##_ccmencryptdone(pre##_ccmctx *ctx, buf *dst, \
543 void *tag, size_t tsz) \
545 octet t[PRE##_BLKSZ]; \
547 /* Some initial checks. */ \
548 if (!BOK(dst)) return (-1); \
550 /* Calculate and return the tag. */ \
551 pre##_ccmtag(ctx, t, tsz); \
552 memcpy(tag, t, tsz); \
558 /* --- @pre_ccmdecryptdone@ --- * \
560 * Arguments: @pre_ccmctx *ctx@ = pointer to an CCM context \
561 * @buf *dst@ = buffer for remaining plaintext \
562 * @const void *tag@ = tag to verify \
563 * @size_t tsz@ = length of tag \
565 * Returns: @+1@ for complete success; @0@ if tag verification \
566 * failed; @-1@ for other kinds of errors. \
568 * Use: Completes an CCM decryption operation. The @aad@ \
569 * pointer may be null if there is no additional \
570 * authenticated data. CCM doesn't buffer plaintext, but \
571 * the output buffer is provided anyway for consistency \
572 * with other AEAD schemes which don't have this property; \
573 * the function will fail if the output buffer is broken. \
576 int pre##_ccmdecryptdone(pre##_ccmctx *ctx, buf *dst, \
577 const void *tag, size_t tsz) \
579 octet t[PRE##_BLKSZ]; \
581 /* Some initial checks. */ \
582 if (!BOK(dst)) return (-1); \
584 /* Calculate and check the tag. */ \
585 pre##_ccmtag(ctx, t, tsz); \
586 if (!ct_memeq(tag, t, tsz)) return (0); \
590 /* --- Generic AEAD interface --- */ \
592 typedef struct gctx { \
597 static void gahash(gaead_aad *a, const void *h, size_t hsz) \
598 { gctx *ctx = (gctx *)a; pre##_ccmaadhash(&ctx->ctx, h, hsz); } \
600 static void gadestroy(gaead_aad *a) { ; } \
602 static const gaead_aadops gaops = \
603 { &pre##_ccm, 0, gahash, gadestroy }; \
605 typedef struct gectx { \
610 static gaead_aad *geaad(gaead_enc *e) \
611 { gectx *enc = (gectx *)e; return (&enc->g.a); } \
613 static int gereinit(gaead_enc *e, const void *n, size_t nsz, \
614 size_t hsz, size_t msz, size_t tsz) \
616 gectx *enc = (gectx *)e; \
617 return (pre##_ccmreinit(&enc->g.ctx, n, nsz, hsz, msz, tsz)); \
620 static int geenc(gaead_enc *e, const void *m, size_t msz, buf *b) \
622 gectx *enc = (gectx *)e; \
623 return (pre##_ccmencrypt(&enc->g.ctx, m, msz, b)); \
626 static int gedone(gaead_enc *e, const gaead_aad *a, \
627 buf *b, void *t, size_t tsz) \
629 gectx *enc = (gectx *)e; \
630 assert((!a && !enc->g.ctx.p.hsz) || a == &enc->g.a); \
631 return (pre##_ccmencryptdone(&enc->g.ctx, b, t, tsz)); \
634 static void gedestroy(gaead_enc *e) \
635 { gectx *enc = (gectx *)e; BURN(*enc); S_DESTROY(enc); } \
637 static const gaead_encops geops = \
638 { &pre##_ccm, geaad, gereinit, geenc, gedone, gedestroy }; \
640 typedef struct gdctx { \
645 static gaead_aad *gdaad(gaead_dec *d) \
646 { gdctx *dec = (gdctx *)d; return (&dec->g.a); } \
648 static int gdreinit(gaead_dec *d, const void *n, size_t nsz, \
649 size_t hsz, size_t csz, size_t tsz) \
651 gdctx *dec = (gdctx *)d; \
652 return (pre##_ccmreinit(&dec->g.ctx, n, nsz, hsz, csz, tsz)); \
655 static int gddec(gaead_dec *d, const void *c, size_t csz, buf *b) \
657 gdctx *dec = (gdctx *)d; \
658 return (pre##_ccmdecrypt(&dec->g.ctx, c, csz, b)); \
661 static int gddone(gaead_dec *d, const gaead_aad *a, \
662 buf *b, const void *t, size_t tsz) \
664 gdctx *dec = (gdctx *)d; \
665 assert((!a && !dec->g.ctx.p.hsz) || a == &dec->g.a); \
666 return (pre##_ccmdecryptdone(&dec->g.ctx, b, t, tsz)); \
669 static void gddestroy(gaead_dec *d) \
670 { gdctx *dec = (gdctx *)d; BURN(*dec); S_DESTROY(dec); } \
672 static const gaead_decops gdops = \
673 { &pre##_ccm, gdaad, gdreinit, gddec, gddone, gddestroy }; \
675 typedef struct gkctx { \
680 static gaead_enc *gkenc(const gaead_key *k, const void *n, size_t nsz, \
681 size_t hsz, size_t msz, size_t tsz) \
683 gkctx *key = (gkctx *)k; \
684 gectx *enc = S_CREATE(gectx); \
686 enc->e.ops = &geops; enc->g.a.ops = &gaops; \
687 if (pre##_ccminit(&enc->g.ctx, &key->key, n, nsz, hsz, msz, tsz)) \
688 { gedestroy(&enc->e); return (0); } \
692 static gaead_dec *gkdec(const gaead_key *k, const void *n, size_t nsz, \
693 size_t hsz, size_t csz, size_t tsz) \
695 gkctx *key = (gkctx *)k; \
696 gdctx *dec = S_CREATE(gdctx); \
698 dec->d.ops = &gdops; dec->g.a.ops = &gaops; \
699 if (pre##_ccminit(&dec->g.ctx, &key->key, n, nsz, hsz, csz, tsz)) \
700 { gddestroy(&dec->d); return (0); } \
704 static void gkdestroy(gaead_key *k) \
705 { gkctx *key = (gkctx *)k; BURN(*key); S_DESTROY(key); } \
707 static const gaead_keyops gkops = \
708 { &pre##_ccm, 0, gkenc, gkdec, gkdestroy }; \
710 static gaead_key *gckey(const void *k, size_t ksz) \
712 gkctx *key = S_CREATE(gkctx); \
713 key->k.ops = &gkops; \
714 pre##_init(&key->key, k, ksz); \
718 const gcaead pre##_ccm = { \
720 pre##_keysz, pre##_ccmnoncesz, pre##_ccmtagsz, \
722 AEADF_PCHSZ | AEADF_PCMSZ | AEADF_PCTSZ | \
723 AEADF_AADNDEP | AEADF_AADFIRST, \
727 CCM_TESTX(PRE, pre, name, fname)
729 /*----- Test rig ----------------------------------------------------------*/
731 #define CCM_TEST(PRE, pre) CCM_TESTX(PRE, pre, #pre, #pre)
733 /* --- @CCM_TEST@ --- *
735 * Arguments: @PRE, pre@ = prefixes for the underlying block cipher
737 * Use: Standard test rig for CCM functions.
744 #include <mLib/dstr.h>
745 #include <mLib/macros.h>
746 #include <mLib/quis.h>
747 #include <mLib/testrig.h>
749 #define CCM_TESTX(PRE, pre, name, fname) \
751 static int ccmverify(dstr *v) \
758 int szs[] = { 1, 7, 192, -1, 0 }, *ip; \
760 dstr d = DSTR_INIT, t = DSTR_INIT; \
763 dstr_ensure(&d, v[4].len > v[3].len ? v[4].len : v[3].len); \
764 dstr_ensure(&t, v[5].len); t.len = v[5].len; \
766 pre##_init(&key, v[0].buf, v[0].len); \
768 for (ip = szs; *ip; ip++) { \
770 pre##_ccminit(&ctx, &key, (octet *)v[1].buf, v[1].len, \
771 v[2].len, v[3].len, v[5].len); \
775 if (i == -1) i = hsz; \
776 if (i > hsz) continue; \
777 p = (octet *)v[2].buf; \
779 if (i > hsz) i = hsz; \
780 pre##_ccmaadhash(&ctx, p, i); \
784 buf_init(&b, d.buf, d.sz); \
787 if (i == -1) i = msz; \
788 if (i > msz) continue; \
789 p = (octet *)v[3].buf; \
791 if (i > msz) i = msz; \
792 if (pre##_ccmencrypt(&ctx, p, i, &b)) { \
793 puts("!! ccmencrypt reports failure"); \
799 if (pre##_ccmencryptdone(&ctx, &b, (octet *)t.buf, t.len)) { \
800 puts("!! ccmencryptdone reports failure"); \
805 if (d.len != v[4].len || \
806 MEMCMP(d.buf, !=, v[4].buf, v[4].len) || \
807 MEMCMP(t.buf, !=, v[5].buf, v[5].len)) { \
809 printf("\nfail encrypt:\n\tstep = %i", *ip); \
810 fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout); \
811 fputs("\n\tnonce = ", stdout); type_hex.dump(&v[1], stdout); \
812 fputs("\n\theader = ", stdout); type_hex.dump(&v[2], stdout); \
813 fputs("\n\tmessage = ", stdout); type_hex.dump(&v[3], stdout); \
814 fputs("\n\texp ct = ", stdout); type_hex.dump(&v[4], stdout); \
815 fputs("\n\tcalc ct = ", stdout); type_hex.dump(&d, stdout); \
816 fputs("\n\texp tag = ", stdout); type_hex.dump(&v[5], stdout); \
817 fputs("\n\tcalc tag = ", stdout); type_hex.dump(&t, stdout); \
822 pre##_ccminit(&ctx, &key, (octet *)v[1].buf, v[1].len, \
823 v[2].len, v[4].len, v[5].len); \
827 if (i == -1) i = hsz; \
828 if (i > hsz) continue; \
829 p = (octet *)v[2].buf; \
831 if (i > hsz) i = hsz; \
832 pre##_ccmaadhash(&ctx, p, i); \
836 buf_init(&b, d.buf, d.sz); \
839 if (i == -1) i = msz; \
840 if (i > msz) continue; \
841 p = (octet *)v[4].buf; \
843 if (i > msz) i = msz; \
844 if (pre##_ccmdecrypt(&ctx, p, i, &b)) { \
845 puts("!! ccmdecrypt reports failure"); \
846 win = 0; goto fail_dec; \
851 win = pre##_ccmdecryptdone(&ctx, &b, (octet *)v[5].buf, v[5].len); \
853 puts("!! ccmdecryptdone reports failure"); \
858 if (d.len != v[3].len || !win || \
859 MEMCMP(d.buf, !=, v[3].buf, v[3].len)) { \
861 printf("\nfail decrypt:\n\tstep = %i", *ip); \
862 fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout); \
863 fputs("\n\tnonce = ", stdout); type_hex.dump(&v[1], stdout); \
864 fputs("\n\theader = ", stdout); type_hex.dump(&v[2], stdout); \
865 fputs("\n\tciphertext = ", stdout); type_hex.dump(&v[4], stdout); \
866 fputs("\n\texp pt = ", stdout); type_hex.dump(&v[3], stdout); \
867 fputs("\n\tcalc pt = ", stdout); type_hex.dump(&d, stdout); \
868 fputs("\n\ttag = ", stdout); type_hex.dump(&v[5], stdout); \
869 printf("\n\tverify %s", win ? "ok" : "FAILED"); \
875 dstr_destroy(&d); dstr_destroy(&t); \
879 static test_chunk aeaddefs[] = { \
880 { name "-ccm", ccmverify, \
881 { &type_hex, &type_hex, &type_hex, &type_hex, \
882 &type_hex, &type_hex, 0 } }, \
886 int main(int argc, char *argv[]) \
889 test_run(argc, argv, aeaddefs, SRCDIR"/t/" fname); \
894 # define CCM_TESTX(PRE, pre, name, fname)
897 /*----- That's all, folks -------------------------------------------------*/