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/macros.h>
472 #include <mLib/testrig.h>
476 static strobe_ctx states
[NSTATE
];
478 static void dump(int rc
, char win
, const void *p
, size_t sz
)
487 if (!rc
) putchar(win
);
490 for (i
= 0, printable
= 1; i
< sz
; i
++)
491 if (!ISPRINT(q
[i
])) { printable
= 0; break; }
495 hex
= hex_class
.encoder(CDCF_LOWERC
, 0, 0);
496 hex
->ops
->code(hex
, p
, sz
, &d
);
497 dstr_write(&d
, stdout
);
498 hex
->ops
->destroy(hex
);
505 typedef int opfunc(strobe_ctx
*, unsigned, const void *, void *, size_t);
507 static int op_init(strobe_ctx
*ctx
, unsigned f
,
508 const void *p
, void *q
, size_t sz
)
509 { strobe_init(ctx
, sz
); return (0); }
511 static int op_copy(strobe_ctx
*ctx
, unsigned f
,
512 const void *p
, void *q
, size_t sz
)
513 { *ctx
= states
[sz
]; return (0); }
515 static int op_begin(strobe_ctx
*ctx
, unsigned f
,
516 const void *p
, void *q
, size_t sz
)
517 { strobe_begin(ctx
, f
); return (0); }
519 static int op_process(strobe_ctx
*ctx
, unsigned f
,
520 const void *p
, void *q
, size_t sz
)
521 { strobe_process(ctx
, p
, q
, sz
); return (0); }
523 static int op_done(strobe_ctx
*ctx
, unsigned f
,
524 const void *p
, void *q
, size_t sz
)
525 { return (strobe_done(ctx
)); }
527 static int op_key(strobe_ctx
*ctx
, unsigned f
,
528 const void *p
, void *q
, size_t sz
)
529 { strobe_key(ctx
, f
, p
, sz
); return (0); }
531 static int op_ad(strobe_ctx
*ctx
, unsigned f
,
532 const void *p
, void *q
, size_t sz
)
533 { strobe_ad(ctx
, f
, p
, sz
); return (0); }
535 static int op_prf(strobe_ctx
*ctx
, unsigned f
,
536 const void *p
, void *q
, size_t sz
)
537 { strobe_prf(ctx
, f
, q
, sz
); return (0); }
539 static int op_clrout(strobe_ctx
*ctx
, unsigned f
,
540 const void *p
, void *q
, size_t sz
)
541 { strobe_clrout(ctx
, f
, p
, sz
); return (0); }
543 static int op_clrin(strobe_ctx
*ctx
, unsigned f
,
544 const void *p
, void *q
, size_t sz
)
545 { strobe_clrin(ctx
, f
, p
, sz
); return (0); }
547 static int op_encout(strobe_ctx
*ctx
, unsigned f
,
548 const void *p
, void *q
, size_t sz
)
549 { strobe_encout(ctx
, f
, p
, q
, sz
); return (0); }
551 static int op_encin(strobe_ctx
*ctx
, unsigned f
,
552 const void *p
, void *q
, size_t sz
)
553 { strobe_encin(ctx
, f
, p
, q
, sz
); return (0); }
555 static int op_macout(strobe_ctx
*ctx
, unsigned f
,
556 const void *p
, void *q
, size_t sz
)
557 { strobe_macout(ctx
, f
, q
, sz
); return (0); }
559 static int op_macin(strobe_ctx
*ctx
, unsigned f
,
560 const void *p
, void *q
, size_t sz
)
561 { return (strobe_macin(ctx
, f
, p
, sz
)); }
563 static int op_ratchet(strobe_ctx
*ctx
, unsigned f
,
564 const void *p
, void *q
, size_t sz
)
565 { strobe_ratchet(ctx
, f
, sz
); return (0); }
567 static const struct optab
{
571 #define OP(op) { #op, op_##op }
573 OP(begin
), OP(process
), OP(done
),
574 OP(key
), OP(ad
), OP(prf
),
575 OP(clrout
), OP(clrin
),
576 OP(encout
), OP(encin
),
577 OP(macout
), OP(macin
),
583 static int verify(dstr v
[])
589 const struct optab
*op
;
590 dstr d0
= DSTR_INIT
, d1
= DSTR_INIT
;
593 const void *src
, *destref
;
599 /* First, get the register number. */
600 r
= *(int *)v
[0].buf
; ctx
= &states
[r
];
602 /* Next job is to parse the command and flags. */
603 q
= v
[1].buf
; p
= q
; q
+= strcspn(q
, "/"); if (*q
) *q
++ = 0;
604 for (op
= optab
; op
->name
; op
++)
605 if (STRCMP(op
->name
, ==, p
)) goto found_op
;
610 for (p
= q
; *p
; p
++) {
612 case 'I': f
|= STRBF_I
; break;
613 case 'C': f
|= STRBF_C
; break;
614 case 'A': f
|= STRBF_A
; break;
615 case 'T': f
|= STRBF_T
; break;
616 case 'M': f
|= STRBF_M
; break;
621 /* Convert the source parameter. */
624 { src
= 0; sz
= strtoul(p
+ 1, 0, 0); }
626 { src
= p
+ 1; sz
= v
[2].len
- 1; }
627 else if (*p
== '!') {
628 hex
= hex_class
.decoder(CDCF_IGNCASE
);
629 rc
= hex
->ops
->code(hex
, p
+ 1, v
[2].len
- 1, &d0
); assert(!rc
);
630 src
= d0
.buf
; sz
= d0
.len
;
631 hex
->ops
->destroy(hex
);
635 /* Convert the destination parameter. */
638 { destref
= 0; rcref
= 0; assert(v
[3].len
== 1); }
640 { destref
= 0; rcref
= -1; assert(v
[3].len
== 1); }
642 { destref
= p
+ 1; assert(sz
== v
[3].len
- 1); rcref
= 0; }
643 else if (*p
== '!') {
644 hex
= hex_class
.decoder(CDCF_IGNCASE
);
645 rc
= hex
->ops
->code(hex
, p
+ 1, v
[3].len
- 1, &d1
); assert(!rc
);
646 destref
= d1
.buf
; assert(sz
== d1
.len
);
647 hex
->ops
->destroy(hex
);
651 if (!destref
) dest
= 0;
652 else dest
= xmalloc(sz
);
654 /* Do the operation. */
655 rc
= op
->op(ctx
, f
, src
, dest
, sz
);
657 /* Check we got the right answer. */
658 ok
= (rc
== rcref
&& (!destref
|| MEMCMP(dest
, ==, destref
, sz
)));
660 printf("failed test\n");
661 printf(" state = %d\n", r
);
662 printf(" operation = %s%s%s%s%s%s%s\n",
665 f
&STRBF_I ?
"I" : "",
666 f
&STRBF_A ?
"A" : "",
667 f
&STRBF_C ?
"C" : "",
668 f
&STRBF_T ?
"T" : "",
669 f
&STRBF_M ?
"M" : "");
670 printf(" input = "); dump(0, '*', src
, sz
);
671 printf(" computed = "); dump(rc
, '+', dest
, sz
);
672 printf(" expected = "); dump(rcref
, '+', destref
, sz
);
681 static test_chunk tests
[] = {
683 { &type_int
, &type_string
, &type_string
, &type_string
, 0 } },
687 int main(int argc
, char *argv
[])
689 test_run(argc
, argv
, tests
, SRCDIR
"/t/strobe");
695 /*----- That's all, folks -------------------------------------------------*/