3 * The STROBE protocol framework
5 * (c) 2018 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Catacomb.
12 * Catacomb is free software: you can redistribute it and/or modify it
13 * under the terms of the GNU Library General Public License as published
14 * by the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * Catacomb is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * 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 Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
28 /*----- Header files ------------------------------------------------------*/
36 #include "keccak1600.h"
39 /*----- Magic constants ---------------------------------------------------*/
44 /*----- Utilities ---------------------------------------------------------*/
48 * Arguments: @strobe_ctx *ctx@ = pointer to context block to initialize
52 * Use: Cycle the Keccak-p[1600, n] duplex function.
55 static void crank(strobe_ctx
*ctx
)
61 /* Ensure that we've not overstepped the rate bound. */
62 assert(ctx
->n
<= ctx
->r
- 2);
64 /* Apply the cSHAKE and rate padding. */
65 ctx
->buf
[ctx
->n
] ^= ctx
->n0
;
66 ctx
->buf
[ctx
->n
+ 1] ^= DDATA
;
67 ctx
->buf
[ctx
->r
- 1] ^= DRATE
;
69 /* Cycle the sponge. */
70 for (i
= 0, p
= ctx
->buf
; i
< ctx
->r
/8; i
++)
71 { LOAD64_L_(t
[i
], p
); p
+= 8; }
72 keccak1600_set(&ctx
->k
, t
, ctx
->r
/8);
73 keccak1600_p(&ctx
->k
, &ctx
->k
, 24);
74 keccak1600_extract(&ctx
->k
, t
, ctx
->r
/8);
75 for (i
= 0, p
= ctx
->buf
; i
< ctx
->r
/8; i
++)
76 { STORE64_L_(p
, t
[i
]); p
+= 8; }
78 /* Restart at the beginning of the buffer, and note this as a
86 * Arguments: @octet *z@ = pointer to output buffer
87 * @const octet *x, *y@ = pointer to input buffers
88 * @size_t sz@ = common buffer length
92 * Use: Store the bytewise XOR of the buffers @x@ and @y@ in @z@.
93 * The @x@ and @y@ may be equal, but otherwise the buffers must
97 static void xorbuf(octet
*z
, const octet
*x
, const octet
*y
, size_t sz
)
98 { size_t i
; for (i
= 0; i
< sz
; i
++) *z
++ = *x
++ ^ *y
++; }
100 /* --- @nonzerop@ --- *
102 * Arguments: @const octet *x@ = pointer to input buffer
103 * @size_t sz@ = buffer length
107 * Use: If any byte of @x@ is nonzero, then return a nonzero value
108 * between 1 and 255 inclusive; otherwise return zero.
111 static unsigned nonzerop(const octet
*x
, size_t sz
)
116 for (i
= 0; i
< sz
; i
++) z
|= *x
++;
120 /* --- @unequalp@ --- *
122 * Arguments: @const octet *x, *y@ = pointer to input buffers
123 * @size_t sz@ = common buffer length
127 * Use: If any respective bytes of @x@ and @y@ are unequal, then
128 * return a nonzero value between 1 and 255 inclusive; otherwise
132 static unsigned unequalp(const octet
*x
, const octet
*y
, size_t sz
)
137 for (i
= 0; i
< sz
; i
++) z
|= *x
++ ^ *y
++;
141 /* --- @process_buffer@ --- *
143 * Arguments: @strobe_ctx *ctx@ = pointer to context block
144 * @const octet *p@ = pointer to input buffer
145 * @octet *q@ = pointer to output buffer
146 * @size_t sz@ = common buffer length
150 * Use: Process a portion of a STROBE input small enough to be
151 * satisfied from the internal buffer.
154 static void process_buffer(strobe_ctx
*ctx
,
155 const octet
*p
, octet
*q
, size_t sz
)
157 octet
*b
= ctx
->buf
+ ctx
->n
;
160 if (!(ctx
->f
&STRBF_CRYPTO
)) {
161 /* No crypto to do. The `output' would be equal to the input, so that's
162 * rather uninteresting (and, indeed, forbidden). If there's input, then
163 * mix it into the state.
166 if (p
&& (ctx
->f
&STRBF_VRFOUT
)) z
|= nonzerop(p
, sz
);
167 if (p
) xorbuf(b
, b
, p
, sz
);
168 } else if (!(ctx
->f
&STRBF_MIXOUT
)) {
169 /* Mix the input into the sponge state. That means that the new state
170 * will be equal to the output.
173 if (p
) xorbuf(b
, b
, p
, sz
);
174 if (ctx
->f
&STRBF_VRFOUT
) z
|= nonzerop(b
, sz
);
175 if (q
) memcpy(q
, b
, sz
);
177 /* Mix the output into the sponge state, so the new state will in fact be
178 * equal to the input. If the input and output buffers are equal then we
179 * have a dance to do.
183 if (ctx
->f
&STRBF_VRFOUT
) z
|= unequalp(p
, b
, sz
);
187 if (q
!= p
) memcpy(b
, p
, sz
);
188 else xorbuf(b
, b
, q
, sz
);
189 if (ctx
->f
&STRBF_VRFOUT
) z
|= nonzerop(q
, sz
);
192 /* As above, only the input is hardwired to zero. That means that we
193 * copy state bytes to the output (if any), and just clobber the state
197 if (q
) memcpy(q
, b
, sz
);
201 /* Set the @STRBF_NZERO@ flag if @z@ is nonzero. If @z@ is zero then
202 * subtracting one will set all of its bits, so, in particular, bits
203 * 8--15. Otherwise, @z@ is between 1 and 255, so bits 8--15 are clear and
204 * will remain so when we subtract one.
206 if (ctx
->f
&STRBF_VRFOUT
) ctx
->f
|= ((z
- 1)&STRBF_NZERO
) ^ STRBF_NZERO
;
208 /* Update the buffer cursor. */
212 /*----- Interface ---------------------------------------------------------*/
214 /* --- @strobe_init@ --- *
216 * Arguments: @strobe_ctx *ctx@ = pointer to context block to initialize
217 * @unsigned lambda@ = security parameter, in bits (must be a
222 * Use: Initialize a STROBE context for use.
225 void strobe_init(strobe_ctx
*ctx
, unsigned lambda
)
227 const char v
[] = "STROBEv1.0.2";
233 /* Check the security parameter. */
234 assert(lambda
%32 == 0); assert(lambda
<= 704);
235 ctx
->r
= (1600 - 2*lambda
)/8;
237 /* Set up the initial cSHAKE framing. */
238 buf_init(&b
, ctx
->buf
, ctx
->r
);
239 buf_putu8(&b
, 1); buf_putu8(&b
, ctx
->r
);
240 buf_putu8(&b
, 1); buf_putu8(&b
, 0);
241 buf_putu8(&b
, 1); buf_putu8(&b
, 8*(sizeof(v
) - 1));
242 buf_put(&b
, v
, sizeof(v
) - 1);
244 n
= BLEN(&b
); if (n
%8) memset(ctx
->buf
+ n
, 0, 8 - n
%8);
246 /* Cycle the sponge once initially, and get the first output buffer. */
247 keccak1600_init(&ctx
->k
);
248 for (i
= 0, p
= ctx
->buf
; i
< (n
+ 7)/8; i
++)
249 { LOAD64_L_(t
[i
], p
); p
+= 8; }
250 keccak1600_set(&ctx
->k
, t
, (n
+ 7)/8);
251 keccak1600_p(&ctx
->k
, &ctx
->k
, 24);
252 keccak1600_extract(&ctx
->k
, t
, ctx
->r
/8);
253 for (i
= 0, p
= ctx
->buf
; i
< ctx
->r
/8; i
++)
254 { STORE64_L_(p
, t
[i
]); p
+= 8; }
256 /* Initialize the other parts of the state. */
257 ctx
->n
= ctx
->n0
= 0; ctx
->f
= 0;
260 /* --- @strobe_begin@ --- *
262 * Arguments: @strobe_ctx *ctx@ = pointer to context block
263 * @unsigned op@ = bitmask of flags
267 * Use: Begin a STROBE operation. The flags determine the behaviour
268 * of the @strobe_process@ and @strobe_done@ functions.
270 * * The @I@ bit determines the primary direction of data
271 * movement. If it's clear, data comes from the application
272 * into STROBE. If it's set, data comes from STROBE towards
275 * * The @C@ bit activates cryptographic processing. If it's
276 * clear, then the input and output data would be equal, so
277 * @dest@ must be null. If it's set, then input data is
278 * XORed with the keystream on its way to the output.
280 * * The @A@ bit determines whether the application is
281 * engaged. If it's set, then the input or output buffer
282 * (according to whether @I@ is clear or set, respectively)
283 * holds the application data. If it's clear, and @I@ is
284 * clear, then zero bytes are fed in; if @I@ is set, then
285 * the output is compared with zero, and @strobe_done@
286 * reports the outcome of this comparison.
288 * * The @T@ bit determines whether the transport is engaged.
289 * If it's set, then the input or output buffer (according
290 * to whether @I@ is set or clear, respectively) holds
291 * transport data. If it's clear, and @I@ is set, then zero
292 * bytes are fed in; if @I@ is clear, then the output is
295 * * The @M@ bit marks the data as metadata, but has no other
299 void strobe_begin(strobe_ctx
*ctx
, unsigned op
)
301 /* Preliminary checking. We shouldn't have an operation underway, and the
302 * operation shouldn't have reserved bits set.
304 assert(!(ctx
->f
&STRBF_ACTIVE
)); assert(!(op
&~STRBF_VALIDMASK
));
306 /* Reset our operation state. */
307 ctx
->f
&= STRBF_STMASK
;
309 /* Operation framing. Chain back to the start of the previous frame and
310 * write the new operation code. Set the sticky asymmetry bit here if
313 ctx
->buf
[ctx
->n
++] ^= ctx
->n0
; ctx
->n0
= ctx
->n
;
314 if (ctx
->n
>= ctx
->r
- 2) crank(ctx
);
316 ctx
->buf
[ctx
->n
++] ^= U8(op
);
318 if (!(ctx
->f
&STRBF_INIT
)) ctx
->f
|= STRBF_INIT
| (op
&STRBF_I
);
319 ctx
->buf
[ctx
->n
++] ^= U8(op
^ ctx
->f
);
321 if (ctx
->n
>= ctx
->r
- 2 || (op
&STRBF_C
)) crank(ctx
);
323 /* The operation is now underway. */
324 ctx
->f
|= STRBF_ACTIVE
;
326 /* Determine whether we expect input and/or output. */
327 if (op
&(op
&STRBF_I ? STRBF_T
: STRBF_A
))
328 ctx
->f
|= STRBF_WANTIN
;
329 if ((op
&STRBF_C
) && op
&(op
&STRBF_I ? STRBF_A
: STRBF_T
))
330 ctx
->f
|= STRBF_WANTOUT
;
332 /* Determine whether the keystream is engaged, and how it fits in. */
334 ctx
->f
|= STRBF_CRYPTO
;
335 if ((op
&(STRBF_I
| STRBF_T
)) != STRBF_T
) ctx
->f
|= STRBF_MIXOUT
;
338 /* Determine whether the output is supposed to be all-bytes-zero. */
339 if ((op
&(STRBF_I
| STRBF_A
| STRBF_T
)) == (STRBF_I
| STRBF_T
))
340 ctx
->f
|= STRBF_VRFOUT
;
342 /* The operation is now underway. */
343 ctx
->f
|= STRBF_ACTIVE
;
346 /* --- @strobe_process@ --- *
348 * Arguments: @strobe_ctx *ctx@ = pointer to context block
349 * @const void *src@ = pointer to input data, or null
350 * @void *dest@ = pointer to output data, or null
351 * @size_t sz@ = common buffer length
355 * Use: Process data through the active STROBE operation. The exact
356 * behaviour depends on the flags passed to @strobe_begin@; see
357 * that function for details. If @src@ is null, then the
358 * behaviour is as if the input consists of @sz@ zero bytes. If
359 * @dest@ in null, then the output is discarded.
362 void strobe_process(strobe_ctx
*ctx
, const void *src
, void *dest
, size_t sz
)
364 const octet
*p
= src
; octet
*q
= dest
;
367 /* Make sure that things are set up properly. */
368 assert(ctx
->f
&STRBF_ACTIVE
);
369 if (!(ctx
->f
&STRBF_WANTIN
)) assert(!src
);
370 if (!(ctx
->f
&STRBF_WANTOUT
)) assert(!dest
);
372 /* Work through the input. */
373 spare
= ctx
->r
- ctx
->n
- 2;
375 { process_buffer(ctx
, p
, q
, sz
); return; }
377 process_buffer(ctx
, p
, q
, spare
); crank(ctx
);
378 if (p
) { p
+= spare
; }
379 if (q
) { q
+= spare
; }
383 while (sz
>= ctx
->r
- 2) {
384 process_buffer(ctx
, p
, q
, ctx
->r
- 2); crank(ctx
);
385 if (p
) { p
+= ctx
->r
- 2; }
386 if (q
) { q
+= ctx
->r
- 2; }
389 if (sz
) process_buffer(ctx
, p
, q
, sz
);
392 /* --- @strobe_done@ --- *
394 * Arguments: @strobe_ctx *ctx@ = pointer to context block
396 * Returns: Zero on success; @-1@ on verification failure (if @I@ and @T@
397 * are set and @A@ is clear)
399 * Use: Concludes a STROBE operation, returning the result.
402 int strobe_done(strobe_ctx
*ctx
)
404 assert(ctx
->f
&STRBF_ACTIVE
); ctx
->f
&= ~STRBF_ACTIVE
;
405 if (ctx
->f
&STRBF_VRFOUT
) return (-(int)((ctx
->f
/STRBF_NZERO
)&1u));
409 /* --- @strobe_key@, @strobe_ad@, @strobe_@prf@, @strobe_clrout@,
410 * @strobe_clrin@, @strobe_encout@, @strobe_encin@, @strobe_macout@,
411 * @strobe_macin@, @strobe_ratchet@ --- *
413 * Arguments: @strobe_ctx *ctx@ = pointer to context block
415 * Returns: @strobe_macin@ returns zero on success, or @-1@ on
416 * verification failure
418 * Use: Perform a STROBE operation on a single buffer.
421 static int op(strobe_ctx
*ctx
, unsigned f0
, unsigned f1
,
422 const void *src
, void *dest
, size_t sz
)
424 assert(!(f1
&~STRBF_M
));
426 strobe_begin(ctx
, f0
| f1
);
427 strobe_process(ctx
, src
, dest
, sz
);
428 return (strobe_done(ctx
));
431 void strobe_key(strobe_ctx
*ctx
, unsigned f
, const void *k
, size_t sz
)
432 { op(ctx
, STROBE_KEY
, f
, k
, 0, sz
); }
434 void strobe_ad(strobe_ctx
*ctx
, unsigned f
, const void *h
, size_t sz
)
435 { op(ctx
, STROBE_AD
, f
, h
, 0, sz
); }
437 void strobe_prf(strobe_ctx
*ctx
, unsigned f
, void *t
, size_t sz
)
438 { op(ctx
, STROBE_PRF
, f
, 0, t
, sz
); }
440 void strobe_clrout(strobe_ctx
*ctx
, unsigned f
, const void *m
, size_t sz
)
441 { op(ctx
, STROBE_CLROUT
, f
, m
, 0, sz
); }
443 void strobe_clrin(strobe_ctx
*ctx
, unsigned f
, const void *m
, size_t sz
)
444 { op(ctx
, STROBE_CLRIN
, f
, m
, 0, sz
); }
446 void strobe_encout(strobe_ctx
*ctx
, unsigned f
,
447 const void *m
, void *c
, size_t sz
)
448 { op(ctx
, STROBE_ENCOUT
, f
, m
, c
, sz
); }
450 void strobe_encin(strobe_ctx
*ctx
, unsigned f
,
451 const void *c
, void *m
, size_t sz
)
452 { op(ctx
, STROBE_ENCIN
, f
, c
, m
, sz
); }
454 void strobe_macout(strobe_ctx
*ctx
, unsigned f
, void *t
, size_t sz
)
455 { op(ctx
, STROBE_MACOUT
, f
, 0, t
, sz
); }
457 int strobe_macin(strobe_ctx
*ctx
, unsigned f
, const void *t
, size_t sz
)
458 { return (op(ctx
, STROBE_MACIN
, f
, t
, 0, sz
)); }
460 void strobe_ratchet(strobe_ctx
*ctx
, unsigned f
, size_t sz
)
461 { op(ctx
, STROBE_RATCHET
, f
, 0, 0, sz
); }
463 /*----- Test rig ----------------------------------------------------------*/
470 #include <mLib/hex.h>
471 #include <mLib/testrig.h>
475 static strobe_ctx states
[NSTATE
];
477 static void dump(int rc
, char win
, const void *p
, size_t sz
)
486 if (!rc
) putchar(win
);
489 for (i
= 0, printable
= 1; i
< sz
; i
++)
490 if (!isprint((unsigned char)q
[i
])) { printable
= 0; break; }
494 hex
= hex_class
.encoder(CDCF_LOWERC
, 0, 0);
495 hex
->ops
->code(hex
, p
, sz
, &d
);
496 dstr_write(&d
, stdout
);
497 hex
->ops
->destroy(hex
);
504 typedef int opfunc(strobe_ctx
*, unsigned, const void *, void *, size_t);
506 static int op_init(strobe_ctx
*ctx
, unsigned f
,
507 const void *p
, void *q
, size_t sz
)
508 { strobe_init(ctx
, sz
); return (0); }
510 static int op_copy(strobe_ctx
*ctx
, unsigned f
,
511 const void *p
, void *q
, size_t sz
)
512 { *ctx
= states
[sz
]; return (0); }
514 static int op_begin(strobe_ctx
*ctx
, unsigned f
,
515 const void *p
, void *q
, size_t sz
)
516 { strobe_begin(ctx
, f
); return (0); }
518 static int op_process(strobe_ctx
*ctx
, unsigned f
,
519 const void *p
, void *q
, size_t sz
)
520 { strobe_process(ctx
, p
, q
, sz
); return (0); }
522 static int op_done(strobe_ctx
*ctx
, unsigned f
,
523 const void *p
, void *q
, size_t sz
)
524 { return (strobe_done(ctx
)); }
526 static int op_key(strobe_ctx
*ctx
, unsigned f
,
527 const void *p
, void *q
, size_t sz
)
528 { strobe_key(ctx
, f
, p
, sz
); return (0); }
530 static int op_ad(strobe_ctx
*ctx
, unsigned f
,
531 const void *p
, void *q
, size_t sz
)
532 { strobe_ad(ctx
, f
, p
, sz
); return (0); }
534 static int op_prf(strobe_ctx
*ctx
, unsigned f
,
535 const void *p
, void *q
, size_t sz
)
536 { strobe_prf(ctx
, f
, q
, sz
); return (0); }
538 static int op_clrout(strobe_ctx
*ctx
, unsigned f
,
539 const void *p
, void *q
, size_t sz
)
540 { strobe_clrout(ctx
, f
, p
, sz
); return (0); }
542 static int op_clrin(strobe_ctx
*ctx
, unsigned f
,
543 const void *p
, void *q
, size_t sz
)
544 { strobe_clrin(ctx
, f
, p
, sz
); return (0); }
546 static int op_encout(strobe_ctx
*ctx
, unsigned f
,
547 const void *p
, void *q
, size_t sz
)
548 { strobe_encout(ctx
, f
, p
, q
, sz
); return (0); }
550 static int op_encin(strobe_ctx
*ctx
, unsigned f
,
551 const void *p
, void *q
, size_t sz
)
552 { strobe_encin(ctx
, f
, p
, q
, sz
); return (0); }
554 static int op_macout(strobe_ctx
*ctx
, unsigned f
,
555 const void *p
, void *q
, size_t sz
)
556 { strobe_macout(ctx
, f
, q
, sz
); return (0); }
558 static int op_macin(strobe_ctx
*ctx
, unsigned f
,
559 const void *p
, void *q
, size_t sz
)
560 { return (strobe_macin(ctx
, f
, p
, sz
)); }
562 static int op_ratchet(strobe_ctx
*ctx
, unsigned f
,
563 const void *p
, void *q
, size_t sz
)
564 { strobe_ratchet(ctx
, f
, sz
); return (0); }
566 static const struct optab
{
570 #define OP(op) { #op, op_##op }
572 OP(begin
), OP(process
), OP(done
),
573 OP(key
), OP(ad
), OP(prf
),
574 OP(clrout
), OP(clrin
),
575 OP(encout
), OP(encin
),
576 OP(macout
), OP(macin
),
582 static int verify(dstr v
[])
588 const struct optab
*op
;
589 dstr d0
= DSTR_INIT
, d1
= DSTR_INIT
;
592 const void *src
, *destref
;
598 /* First, get the register number. */
599 r
= *(int *)v
[0].buf
; ctx
= &states
[r
];
601 /* Next job is to parse the command and flags. */
602 q
= v
[1].buf
; p
= q
; q
+= strcspn(q
, "/"); if (*q
) *q
++ = 0;
603 for (op
= optab
; op
->name
; op
++)
604 if (!strcmp(op
->name
, p
)) goto found_op
;
609 for (p
= q
; *p
; p
++) {
611 case 'I': f
|= STRBF_I
; break;
612 case 'C': f
|= STRBF_C
; break;
613 case 'A': f
|= STRBF_A
; break;
614 case 'T': f
|= STRBF_T
; break;
615 case 'M': f
|= STRBF_M
; break;
620 /* Convert the source parameter. */
623 { src
= 0; sz
= strtoul(p
+ 1, 0, 0); }
625 { src
= p
+ 1; sz
= v
[2].len
- 1; }
626 else if (*p
== '!') {
627 hex
= hex_class
.decoder(CDCF_IGNCASE
);
628 rc
= hex
->ops
->code(hex
, p
+ 1, v
[2].len
- 1, &d0
); assert(!rc
);
629 src
= d0
.buf
; sz
= d0
.len
;
630 hex
->ops
->destroy(hex
);
634 /* Convert the destination parameter. */
637 { destref
= 0; rcref
= 0; assert(v
[3].len
== 1); }
639 { destref
= 0; rcref
= -1; assert(v
[3].len
== 1); }
641 { destref
= p
+ 1; assert(sz
== v
[3].len
- 1); rcref
= 0; }
642 else if (*p
== '!') {
643 hex
= hex_class
.decoder(CDCF_IGNCASE
);
644 rc
= hex
->ops
->code(hex
, p
+ 1, v
[3].len
- 1, &d1
); assert(!rc
);
645 destref
= d1
.buf
; assert(sz
== d1
.len
);
646 hex
->ops
->destroy(hex
);
650 if (!destref
) dest
= 0;
651 else dest
= xmalloc(sz
);
653 /* Do the operation. */
654 rc
= op
->op(ctx
, f
, src
, dest
, sz
);
656 /* Check we got the right answer. */
657 ok
= (rc
== rcref
&& (!destref
|| !memcmp(dest
, destref
, sz
)));
659 printf("failed test\n");
660 printf(" state = %d\n", r
);
661 printf(" operation = %s%s%s%s%s%s%s\n",
664 f
&STRBF_I ?
"I" : "",
665 f
&STRBF_A ?
"A" : "",
666 f
&STRBF_C ?
"C" : "",
667 f
&STRBF_T ?
"T" : "",
668 f
&STRBF_M ?
"M" : "");
669 printf(" input = "); dump(0, '*', src
, sz
);
670 printf(" computed = "); dump(rc
, '+', dest
, sz
);
671 printf(" expected = "); dump(rcref
, '+', destref
, sz
);
680 static test_chunk tests
[] = {
682 { &type_int
, &type_string
, &type_string
, &type_string
, 0 } },
686 int main(int argc
, char *argv
[])
688 test_run(argc
, argv
, tests
, SRCDIR
"/t/strobe");
694 /*----- That's all, folks -------------------------------------------------*/