progs/perftest.c: Use from Glibc syscall numbers.
[catacomb] / symm / strobe.c
CommitLineData
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
55static 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
97static 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
111static 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
132static 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
154static 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
225void 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
299void 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
362void 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
402int 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
421static 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
431void strobe_key(strobe_ctx *ctx, unsigned f, const void *k, size_t sz)
432 { op(ctx, STROBE_KEY, f, k, 0, sz); }
433
434void strobe_ad(strobe_ctx *ctx, unsigned f, const void *h, size_t sz)
435 { op(ctx, STROBE_AD, f, h, 0, sz); }
436
437void strobe_prf(strobe_ctx *ctx, unsigned f, void *t, size_t sz)
438 { op(ctx, STROBE_PRF, f, 0, t, sz); }
439
440void strobe_clrout(strobe_ctx *ctx, unsigned f, const void *m, size_t sz)
441 { op(ctx, STROBE_CLROUT, f, m, 0, sz); }
442
443void strobe_clrin(strobe_ctx *ctx, unsigned f, const void *m, size_t sz)
444 { op(ctx, STROBE_CLRIN, f, m, 0, sz); }
445
446void 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
450void 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
454void strobe_macout(strobe_ctx *ctx, unsigned f, void *t, size_t sz)
455 { op(ctx, STROBE_MACOUT, f, 0, t, sz); }
456
457int strobe_macin(strobe_ctx *ctx, unsigned f, const void *t, size_t sz)
458 { return (op(ctx, STROBE_MACIN, f, t, 0, sz)); }
459
460void 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
476static strobe_ctx states[NSTATE];
477
478static 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
505typedef int opfunc(strobe_ctx *, unsigned, const void *, void *, size_t);
506
507static 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
511static int op_copy(strobe_ctx *ctx, unsigned f,
512 const void *p, void *q, size_t sz)
513 { *ctx = states[sz]; return (0); }
514
515static 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
519static 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
523static int op_done(strobe_ctx *ctx, unsigned f,
524 const void *p, void *q, size_t sz)
525 { return (strobe_done(ctx)); }
526
527static 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
531static 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
535static 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
539static 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
543static 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
547static 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
551static 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
555static 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
559static 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
563static 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
567static 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
583static 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();
607found_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
681static test_chunk tests[] = {
682 { "strobe", verify,
683 { &type_int, &type_string, &type_string, &type_string, 0 } },
684 { 0, 0, { 0 } }
685};
686
687int 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 -------------------------------------------------*/