Commit | Line | Data |
---|---|---|
b1d7b424 MW |
1 | /* -*-c-*- |
2 | * | |
3 | * The STROBE protocol framework | |
4 | * | |
5 | * (c) 2018 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of Catacomb. | |
11 | * | |
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. | |
16 | * | |
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. | |
21 | * | |
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, | |
25 | * USA. | |
26 | */ | |
27 | ||
28 | /*----- Header files ------------------------------------------------------*/ | |
29 | ||
30 | #include <assert.h> | |
31 | #include <ctype.h> | |
32 | #include <string.h> | |
33 | ||
34 | #include <mLib/buf.h> | |
35 | ||
36 | #include "keccak1600.h" | |
37 | #include "strobe.h" | |
38 | ||
39 | /*----- Magic constants ---------------------------------------------------*/ | |
40 | ||
41 | #define DDATA 0x04 | |
42 | #define DRATE 0x80 | |
43 | ||
44 | /*----- Utilities ---------------------------------------------------------*/ | |
45 | ||
46 | /* --- @crank@ --- * | |
47 | * | |
48 | * Arguments: @strobe_ctx *ctx@ = pointer to context block to initialize | |
49 | * | |
50 | * Returns: --- | |
51 | * | |
52 | * Use: Cycle the Keccak-p[1600, n] duplex function. | |
53 | */ | |
54 | ||
55 | static void crank(strobe_ctx *ctx) | |
56 | { | |
57 | kludge64 t[25]; | |
58 | octet *p; | |
59 | unsigned i; | |
60 | ||
61 | /* Ensure that we've not overstepped the rate bound. */ | |
62 | assert(ctx->n <= ctx->r - 2); | |
63 | ||
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; | |
68 | ||
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; } | |
77 | ||
78 | /* Restart at the beginning of the buffer, and note this as a | |
79 | * continuation. | |
80 | */ | |
81 | ctx->n = ctx->n0 = 0; | |
82 | } | |
83 | ||
84 | /* --- @xorbuf@ --- * | |
85 | * | |
86 | * Arguments: @octet *z@ = pointer to output buffer | |
87 | * @const octet *x, *y@ = pointer to input buffers | |
88 | * @size_t sz@ = common buffer length | |
89 | * | |
90 | * Returns: --- | |
91 | * | |
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 | |
94 | * not overlap. | |
95 | */ | |
96 | ||
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++; } | |
99 | ||
100 | /* --- @nonzerop@ --- * | |
101 | * | |
102 | * Arguments: @const octet *x@ = pointer to input buffer | |
103 | * @size_t sz@ = buffer length | |
104 | * | |
105 | * Returns: --- | |
106 | * | |
107 | * Use: If any byte of @x@ is nonzero, then return a nonzero value | |
108 | * between 1 and 255 inclusive; otherwise return zero. | |
109 | */ | |
110 | ||
111 | static unsigned nonzerop(const octet *x, size_t sz) | |
112 | { | |
113 | unsigned z = 0; | |
114 | size_t i; | |
115 | ||
116 | for (i = 0; i < sz; i++) z |= *x++; | |
117 | return (z); | |
118 | } | |
119 | ||
120 | /* --- @unequalp@ --- * | |
121 | * | |
122 | * Arguments: @const octet *x, *y@ = pointer to input buffers | |
123 | * @size_t sz@ = common buffer length | |
124 | * | |
125 | * Returns: --- | |
126 | * | |
127 | * Use: If any respective bytes of @x@ and @y@ are unequal, then | |
128 | * return a nonzero value between 1 and 255 inclusive; otherwise | |
129 | * return zero. | |
130 | */ | |
131 | ||
132 | static unsigned unequalp(const octet *x, const octet *y, size_t sz) | |
133 | { | |
134 | unsigned z = 0; | |
135 | size_t i; | |
136 | ||
137 | for (i = 0; i < sz; i++) z |= *x++ ^ *y++; | |
138 | return (z); | |
139 | } | |
140 | ||
141 | /* --- @process_buffer@ --- * | |
142 | * | |
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 | |
147 | * | |
148 | * Returns: --- | |
149 | * | |
150 | * Use: Process a portion of a STROBE input small enough to be | |
151 | * satisfied from the internal buffer. | |
152 | */ | |
153 | ||
154 | static void process_buffer(strobe_ctx *ctx, | |
155 | const octet *p, octet *q, size_t sz) | |
156 | { | |
157 | octet *b = ctx->buf + ctx->n; | |
158 | unsigned z = 0; | |
159 | ||
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. | |
164 | */ | |
165 | ||
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. | |
171 | */ | |
172 | ||
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); | |
176 | } else if (p) { | |
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. | |
180 | */ | |
181 | ||
182 | if (!q) { | |
183 | if (ctx->f&STRBF_VRFOUT) z |= unequalp(p, b, sz); | |
184 | memcpy(b, p, sz); | |
185 | } else { | |
186 | xorbuf(q, 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); | |
190 | } | |
191 | } else { | |
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 | |
194 | * when we're done. | |
195 | */ | |
196 | ||
197 | if (q) memcpy(q, b, sz); | |
198 | memset(b, 0, sz); | |
199 | } | |
200 | ||
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. | |
205 | */ | |
206 | if (ctx->f&STRBF_VRFOUT) ctx->f |= ((z - 1)&STRBF_NZERO) ^ STRBF_NZERO; | |
207 | ||
208 | /* Update the buffer cursor. */ | |
209 | ctx->n += sz; | |
210 | } | |
211 | ||
212 | /*----- Interface ---------------------------------------------------------*/ | |
213 | ||
214 | /* --- @strobe_init@ --- * | |
215 | * | |
216 | * Arguments: @strobe_ctx *ctx@ = pointer to context block to initialize | |
217 | * @unsigned lambda@ = security parameter, in bits (must be a | |
218 | * multiple of 32) | |
219 | * | |
220 | * Returns: --- | |
221 | * | |
222 | * Use: Initialize a STROBE context for use. | |
223 | */ | |
224 | ||
225 | void strobe_init(strobe_ctx *ctx, unsigned lambda) | |
226 | { | |
227 | const char v[] = "STROBEv1.0.2"; | |
228 | kludge64 t[25]; | |
229 | octet *p; | |
230 | buf b; | |
231 | unsigned n, i; | |
232 | ||
233 | /* Check the security parameter. */ | |
234 | assert(lambda%32 == 0); assert(lambda <= 704); | |
235 | ctx->r = (1600 - 2*lambda)/8; | |
236 | ||
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); | |
243 | assert(BOK(&b)); | |
244 | n = BLEN(&b); if (n%8) memset(ctx->buf + n, 0, 8 - n%8); | |
245 | ||
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; } | |
255 | ||
256 | /* Initialize the other parts of the state. */ | |
257 | ctx->n = ctx->n0 = 0; ctx->f = 0; | |
258 | } | |
259 | ||
260 | /* --- @strobe_begin@ --- * | |
261 | * | |
262 | * Arguments: @strobe_ctx *ctx@ = pointer to context block | |
263 | * @unsigned op@ = bitmask of flags | |
264 | * | |
265 | * Returns: --- | |
266 | * | |
267 | * Use: Begin a STROBE operation. The flags determine the behaviour | |
268 | * of the @strobe_process@ and @strobe_done@ functions. | |
269 | * | |
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 | |
273 | * the application. | |
274 | * | |
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. | |
279 | * | |
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. | |
287 | * | |
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 | |
293 | * discarded. | |
294 | * | |
295 | * * The @M@ bit marks the data as metadata, but has no other | |
296 | * effect. | |
297 | */ | |
298 | ||
299 | void strobe_begin(strobe_ctx *ctx, unsigned op) | |
300 | { | |
301 | /* Preliminary checking. We shouldn't have an operation underway, and the | |
302 | * operation shouldn't have reserved bits set. | |
303 | */ | |
304 | assert(!(ctx->f&STRBF_ACTIVE)); assert(!(op&~STRBF_VALIDMASK)); | |
305 | ||
306 | /* Reset our operation state. */ | |
307 | ctx->f &= STRBF_STMASK; | |
308 | ||
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 | |
311 | * necessary. | |
312 | */ | |
313 | ctx->buf[ctx->n++] ^= ctx->n0; ctx->n0 = ctx->n; | |
314 | if (ctx->n >= ctx->r - 2) crank(ctx); | |
315 | if (!(op&STRBF_T)) | |
316 | ctx->buf[ctx->n++] ^= U8(op); | |
317 | else { | |
318 | if (!(ctx->f&STRBF_INIT)) ctx->f |= STRBF_INIT | (op&STRBF_I); | |
319 | ctx->buf[ctx->n++] ^= U8(op ^ ctx->f); | |
320 | } | |
321 | if (ctx->n >= ctx->r - 2 || (op&STRBF_C)) crank(ctx); | |
322 | ||
323 | /* The operation is now underway. */ | |
324 | ctx->f |= STRBF_ACTIVE; | |
325 | ||
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; | |
331 | ||
332 | /* Determine whether the keystream is engaged, and how it fits in. */ | |
333 | if (op&STRBF_C) { | |
334 | ctx->f |= STRBF_CRYPTO; | |
335 | if ((op&(STRBF_I | STRBF_T)) != STRBF_T) ctx->f |= STRBF_MIXOUT; | |
336 | } | |
337 | ||
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; | |
341 | ||
342 | /* The operation is now underway. */ | |
343 | ctx->f |= STRBF_ACTIVE; | |
344 | } | |
345 | ||
346 | /* --- @strobe_process@ --- * | |
347 | * | |
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 | |
352 | * | |
353 | * Returns: --- | |
354 | * | |
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. | |
360 | */ | |
361 | ||
362 | void strobe_process(strobe_ctx *ctx, const void *src, void *dest, size_t sz) | |
363 | { | |
364 | const octet *p = src; octet *q = dest; | |
365 | unsigned spare; | |
366 | ||
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); | |
371 | ||
372 | /* Work through the input. */ | |
373 | spare = ctx->r - ctx->n - 2; | |
374 | if (sz < spare) | |
375 | { process_buffer(ctx, p, q, sz); return; } | |
376 | if (ctx->n) { | |
377 | process_buffer(ctx, p, q, spare); crank(ctx); | |
378 | if (p) { p += spare; } | |
379 | if (q) { q += spare; } | |
380 | sz -= spare; | |
381 | } | |
382 | ||
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; } | |
387 | sz -= ctx->r - 2; | |
388 | } | |
389 | if (sz) process_buffer(ctx, p, q, sz); | |
390 | } | |
391 | ||
392 | /* --- @strobe_done@ --- * | |
393 | * | |
394 | * Arguments: @strobe_ctx *ctx@ = pointer to context block | |
395 | * | |
396 | * Returns: Zero on success; @-1@ on verification failure (if @I@ and @T@ | |
397 | * are set and @A@ is clear) | |
398 | * | |
399 | * Use: Concludes a STROBE operation, returning the result. | |
400 | */ | |
401 | ||
402 | int strobe_done(strobe_ctx *ctx) | |
403 | { | |
404 | assert(ctx->f&STRBF_ACTIVE); ctx->f &= ~STRBF_ACTIVE; | |
405 | if (ctx->f&STRBF_VRFOUT) return (-(int)((ctx->f/STRBF_NZERO)&1u)); | |
406 | else return (0); | |
407 | } | |
408 | ||
409 | /* --- @strobe_key@, @strobe_ad@, @strobe_@prf@, @strobe_clrout@, | |
410 | * @strobe_clrin@, @strobe_encout@, @strobe_encin@, @strobe_macout@, | |
411 | * @strobe_macin@, @strobe_ratchet@ --- * | |
412 | * | |
413 | * Arguments: @strobe_ctx *ctx@ = pointer to context block | |
414 | * | |
415 | * Returns: @strobe_macin@ returns zero on success, or @-1@ on | |
416 | * verification failure | |
417 | * | |
418 | * Use: Perform a STROBE operation on a single buffer. | |
419 | */ | |
420 | ||
421 | static int op(strobe_ctx *ctx, unsigned f0, unsigned f1, | |
422 | const void *src, void *dest, size_t sz) | |
423 | { | |
424 | assert(!(f1&~STRBF_M)); | |
425 | ||
426 | strobe_begin(ctx, f0 | f1); | |
427 | strobe_process(ctx, src, dest, sz); | |
428 | return (strobe_done(ctx)); | |
429 | } | |
430 | ||
431 | void strobe_key(strobe_ctx *ctx, unsigned f, const void *k, size_t sz) | |
432 | { op(ctx, STROBE_KEY, f, k, 0, sz); } | |
433 | ||
434 | void strobe_ad(strobe_ctx *ctx, unsigned f, const void *h, size_t sz) | |
435 | { op(ctx, STROBE_AD, f, h, 0, sz); } | |
436 | ||
437 | void strobe_prf(strobe_ctx *ctx, unsigned f, void *t, size_t sz) | |
438 | { op(ctx, STROBE_PRF, f, 0, t, sz); } | |
439 | ||
440 | void strobe_clrout(strobe_ctx *ctx, unsigned f, const void *m, size_t sz) | |
441 | { op(ctx, STROBE_CLROUT, f, m, 0, sz); } | |
442 | ||
443 | void strobe_clrin(strobe_ctx *ctx, unsigned f, const void *m, size_t sz) | |
444 | { op(ctx, STROBE_CLRIN, f, m, 0, sz); } | |
445 | ||
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); } | |
449 | ||
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); } | |
453 | ||
454 | void strobe_macout(strobe_ctx *ctx, unsigned f, void *t, size_t sz) | |
455 | { op(ctx, STROBE_MACOUT, f, 0, t, sz); } | |
456 | ||
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)); } | |
459 | ||
460 | void strobe_ratchet(strobe_ctx *ctx, unsigned f, size_t sz) | |
461 | { op(ctx, STROBE_RATCHET, f, 0, 0, sz); } | |
462 | ||
463 | /*----- Test rig ----------------------------------------------------------*/ | |
464 | ||
465 | #ifdef TEST_RIG | |
466 | ||
467 | #include <stdlib.h> | |
468 | #include <string.h> | |
469 | ||
470 | #include <mLib/hex.h> | |
141c1284 | 471 | #include <mLib/macros.h> |
b1d7b424 MW |
472 | #include <mLib/testrig.h> |
473 | ||
474 | #define NSTATE 16 | |
475 | ||
476 | static strobe_ctx states[NSTATE]; | |
477 | ||
478 | static void dump(int rc, char win, const void *p, size_t sz) | |
479 | { | |
480 | dstr d = DSTR_INIT; | |
481 | const char *q = p; | |
482 | size_t i; | |
483 | codec *hex; | |
484 | int printable; | |
485 | ||
486 | if (!p) { | |
487 | if (!rc) putchar(win); | |
488 | else putchar('-'); | |
489 | } else { | |
490 | for (i = 0, printable = 1; i < sz; i++) | |
141c1284 | 491 | if (!ISPRINT(q[i])) { printable = 0; break; } |
b1d7b424 MW |
492 | if (printable) |
493 | printf("`%s'", q); | |
494 | else { | |
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); | |
499 | } | |
500 | } | |
501 | dstr_destroy(&d); | |
502 | putchar('\n'); | |
503 | } | |
504 | ||
505 | typedef int opfunc(strobe_ctx *, unsigned, const void *, void *, size_t); | |
506 | ||
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); } | |
510 | ||
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); } | |
514 | ||
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); } | |
518 | ||
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); } | |
522 | ||
523 | static int op_done(strobe_ctx *ctx, unsigned f, | |
524 | const void *p, void *q, size_t sz) | |
525 | { return (strobe_done(ctx)); } | |
526 | ||
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); } | |
530 | ||
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); } | |
534 | ||
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); } | |
538 | ||
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); } | |
542 | ||
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); } | |
546 | ||
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); } | |
550 | ||
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); } | |
554 | ||
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); } | |
558 | ||
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)); } | |
562 | ||
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); } | |
566 | ||
567 | static const struct optab { | |
568 | const char *name; | |
569 | opfunc *op; | |
570 | } optab[] = { | |
571 | #define OP(op) { #op, op_##op } | |
572 | OP(init), OP(copy), | |
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), | |
578 | OP(ratchet), | |
579 | { 0 } | |
580 | #undef OP | |
581 | }; | |
582 | ||
583 | static int verify(dstr v[]) | |
584 | { | |
585 | int r; | |
586 | strobe_ctx *ctx; | |
587 | const char *p; | |
588 | char *q; | |
589 | const struct optab *op; | |
590 | dstr d0 = DSTR_INIT, d1 = DSTR_INIT; | |
591 | codec *hex; | |
592 | unsigned f; | |
593 | const void *src, *destref; | |
594 | void *dest; | |
595 | size_t sz; | |
596 | int rc, rcref; | |
597 | int ok; | |
598 | ||
599 | /* First, get the register number. */ | |
600 | r = *(int *)v[0].buf; ctx = &states[r]; | |
601 | ||
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++) | |
141c1284 | 605 | if (STRCMP(op->name, ==, p)) goto found_op; |
b1d7b424 MW |
606 | abort(); |
607 | found_op: | |
608 | ||
609 | f = 0; | |
610 | for (p = q; *p; p++) { | |
611 | switch (*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; | |
617 | default: abort(); | |
618 | } | |
619 | } | |
620 | ||
621 | /* Convert the source parameter. */ | |
622 | p = v[2].buf; | |
623 | if (*p == '*') | |
624 | { src = 0; sz = strtoul(p + 1, 0, 0); } | |
625 | else if (*p == '=') | |
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); | |
632 | } else | |
633 | abort(); | |
634 | ||
635 | /* Convert the destination parameter. */ | |
636 | p = v[3].buf; | |
637 | if (*p == '+') | |
638 | { destref = 0; rcref = 0; assert(v[3].len == 1); } | |
639 | else if (*p == '-') | |
640 | { destref = 0; rcref = -1; assert(v[3].len == 1); } | |
641 | else if (*p == '=') | |
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); | |
648 | rcref = 0; | |
649 | } else | |
650 | abort(); | |
651 | if (!destref) dest = 0; | |
652 | else dest = xmalloc(sz); | |
653 | ||
654 | /* Do the operation. */ | |
655 | rc = op->op(ctx, f, src, dest, sz); | |
656 | ||
657 | /* Check we got the right answer. */ | |
141c1284 | 658 | ok = (rc == rcref && (!destref || MEMCMP(dest, ==, destref, sz))); |
b1d7b424 MW |
659 | if (!ok) { |
660 | printf("failed test\n"); | |
661 | printf(" state = %d\n", r); | |
662 | printf(" operation = %s%s%s%s%s%s%s\n", | |
663 | op->name, | |
664 | f ? "/" : "", | |
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); | |
673 | } | |
674 | ||
675 | dstr_destroy(&d0); | |
676 | dstr_destroy(&d1); | |
677 | free(dest); | |
678 | return (ok); | |
679 | } | |
680 | ||
681 | static test_chunk tests[] = { | |
682 | { "strobe", verify, | |
683 | { &type_int, &type_string, &type_string, &type_string, 0 } }, | |
684 | { 0, 0, { 0 } } | |
685 | }; | |
686 | ||
687 | int main(int argc, char *argv[]) | |
688 | { | |
689 | test_run(argc, argv, tests, SRCDIR "/t/strobe"); | |
690 | return (0); | |
691 | } | |
692 | ||
693 | #endif | |
694 | ||
695 | /*----- That's all, folks -------------------------------------------------*/ |