2 * crypto-test.c: common test vector processing
5 * This file is Free Software. It was originally written for secnet.
7 * Copyright 2017, 2019 Mark Wooding
9 * You may redistribute secnet as a whole and/or modify it under the
10 * terms of the GNU General Public License as published by the Free
11 * Software Foundation; either version 3, or (at your option) any
14 * You may redistribute this file and/or modify it under the terms of
15 * the GNU General Public License as published by the Free Software
16 * Foundation; either version 2, or (at your option) any later
19 * This software is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this software; if not, see
26 * https://www.gnu.org/licenses/gpl.html.
40 #include "crypto-test.h"
42 /*----- Utilities ---------------------------------------------------------*/
44 static void *xmalloc(size_t sz)
51 fprintf(stderr, "out of memory!\n");
57 static void *xrealloc(void *p, size_t sz)
61 if (!sz) { free(p); return 0; }
62 else if (!p) return xmalloc(sz);
65 fprintf(stderr, "out of memory!\n");
73 void bail(const char *msg, ...)
77 fprintf(stderr, "unexpected error (line %d): ", lno);
78 vfprintf(stderr, msg, ap);
88 #define LINEBUF_INIT { 0, 0 };
90 static int read_line(struct linebuf *b, FILE *fp)
95 ch = getc(fp); if (ch == EOF) return EOF;
98 b->sz = b->sz ? 2*b->sz : 64;
99 b->p = xrealloc(b->p, b->sz);
101 if (ch == EOF || ch == '\n') { b->p[n++] = 0; return 0; }
107 void parse_hex(uint8_t *b, size_t sz, char *p)
109 size_t n = strlen(p);
113 if (n%2) bail("bad hex (odd number of nibbles)");
114 else if (n/2 != sz) bail("bad hex (want %zu bytes, found %zu)", sz, n/2);
116 for (i = 0; i < 2; i++) {
117 if (!isxdigit((unsigned char)p[i]))
118 bail("bad hex digit `%c'", p[i]);
123 *b++ = strtoul(bb, 0, 16); sz--;
127 void dump_hex(FILE *fp, const uint8_t *b, size_t sz)
128 { while (sz--) fprintf(fp, "%02x", *b++); fputc('\n', fp); }
130 void trivial_regty_init(union regval *v) { ; }
131 void trivial_regty_release(union regval *v) { ; }
133 /* Define some global variables we shouldn't need.
135 * Annoyingly, `secnet.h' declares static pointers and initializes them to
136 * point to some external variables. At `-O0', GCC doesn't optimize these
137 * away, so there's a link-time dependency on these variables. Define them
138 * here, so that `f25519.c' and `f448.c' can find them.
140 * (Later GCC has `-Og', which optimizes without making debugging a
141 * nightmare, but I'm not running that version here. Note that `serpent.c'
142 * doesn't have this problem because it defines its own word load and store
143 * operations to cope with its endian weirdness, whereas the field arithmetic
144 * uses `unaligned.h' which manages to include `secnet.h'.)
147 struct timeval tv_now_global;
149 /* Bletch. sha512.c wants to drag in the world. */
150 void *safe_malloc(size_t size, const char *message) { return xmalloc(size); }
151 list_t *new_closure(closure_t *cl) { abort(); }
152 void dict_add(dict_t *dict, cstring_t key, list_t *val) { abort(); }
154 /* Bletch. util.c is a mess of layers. */
155 int consttime_memeq(const void *s1in, const void *s2in, size_t n)
157 const uint8_t *s1=s1in, *s2=s2in;
158 register volatile uint8_t accumulator=0;
161 accumulator |= (*s1++ ^ *s2++);
163 accumulator |= accumulator >> 4; /* constant-time */
164 accumulator |= accumulator >> 2; /* boolean canonicalisation */
165 accumulator |= accumulator >> 1;
171 /*----- Built-in types ----------------------------------------------------*/
173 /* Signed integer. */
175 static void parse_int(union regval *v, char *p)
180 v->i = strtol(p, &q, 0);
181 if (*q || errno) bail("bad integer `%s'", p);
184 static void dump_int(FILE *fp, const union regval *v)
185 { fprintf(fp, "%ld\n", v->i); }
187 static int eq_int(const union regval *v0, const union regval *v1)
188 { return (v0->i == v1->i); }
190 const struct regty regty_int = {
195 trivial_regty_release
198 /* Unsigned integer. */
200 static void parse_uint(union regval *v, char *p)
205 v->u = strtoul(p, &q, 0);
206 if (*q || errno) bail("bad integer `%s'", p);
209 static void dump_uint(FILE *fp, const union regval *v)
210 { fprintf(fp, "%lu\n", v->u); }
212 static int eq_uint(const union regval *v0, const union regval *v1)
213 { return (v0->u == v1->u); }
215 const struct regty regty_uint = {
220 trivial_regty_release
223 /* Byte string, as hex. */
225 void allocate_bytes(union regval *v, size_t sz)
226 { v->bytes.p = xmalloc(sz); v->bytes.sz = sz; }
228 static void init_bytes(union regval *v) { v->bytes.p = 0; v->bytes.sz = 0; }
230 static void parse_bytes(union regval *v, char *p)
232 size_t n = strlen(p);
234 allocate_bytes(v, n/2);
235 parse_hex(v->bytes.p, v->bytes.sz, p);
238 static void dump_bytes(FILE *fp, const union regval *v)
239 { dump_hex(fp, v->bytes.p, v->bytes.sz); }
241 static int eq_bytes(const union regval *v0, const union regval *v1)
243 return v0->bytes.sz == v1->bytes.sz &&
244 !memcmp(v0->bytes.p, v1->bytes.p, v0->bytes.sz);
247 static void release_bytes(union regval *v) { free(v->bytes.p); }
249 const struct regty regty_bytes = {
257 /* Text strings. Not really intended as an output type. */
259 void allocate_string(union regval *v, size_t sz)
260 { v->str.p = xmalloc(sz + 1); v->str.sz = sz; }
262 static void init_string(union regval *v) { v->str.p = 0; v->str.sz = 0; }
264 static void parse_string(union regval *v, char *p)
266 size_t n = strlen(p);
268 allocate_string(v, n);
269 memcpy(v->str.p, p, n + 1);
272 static void dump_string(FILE *fp, const union regval *v)
274 if (v->str.p) fprintf(fp, "`%s'\n", v->str.p);
275 else fputs("nil\n", fp);
278 static int eq_string(const union regval *v0, const union regval *v1)
280 size_t n0 = v0->str.sz, n1 = v1->str.sz, n = n0 < n1 ? n0 : n1;
281 return !strncmp(v0->str.p, v1->str.p, n);
284 static void release_string(union regval *v) { free(v->str.p); }
286 const struct regty regty_string = {
294 /*----- Core test machinery -----------------------------------------------*/
296 /* Say that a register is `reset' by releasing and then re-initializing it.
297 * While there is a current test, all of that test's registers are
298 * initialized. The input registers are reset at the end of `check', ready
299 * for the next test to load new values. The output registers are reset at
300 * the end of `check_test_output', so that a test runner can run a test
301 * multiple times against the same test input, but with different context
305 #define REG(rvec, i) \
306 ((struct reg *)((unsigned char *)state->rvec + (i)*state->regsz))
308 void check_test_output(struct test_state *state, const struct test *test)
310 const struct regdef *def;
311 struct reg *reg, *in, *out;
315 for (def = test->regs; def->name; def++) {
316 if (def->i >= state->nrout) continue;
317 in = REG(in, def->i); out = REG(out, def->i);
318 if (!def->ty->eq(&in->v, &out->v)) ok = 0;
323 printf("failed test `%s'\n", test->name);
324 for (def = test->regs; def->name; def++) {
325 in = REG(in, def->i);
326 if (!(in->f®F_LIVE)) continue;
327 if (def->i >= state->nrout) {
328 printf("\t input `%s' = ", def->name);
329 def->ty->dump(stdout, &in->v);
331 out = REG(out, def->i);
332 match = def->ty->eq(&in->v, &out->v);
333 printf("\t%s `%s' = ",
334 match ? " output" : "expected", def->name);
335 def->ty->dump(stdout, &in->v);
337 printf("\tcomputed `%s' = ", def->name);
338 def->ty->dump(stdout, &out->v);
345 for (def = test->regs; def->name; def++) {
346 if (def->i >= state->nrout) continue;
347 reg = REG(out, def->i);
348 def->ty->release(®->v); def->ty->init(®->v);
352 void run_test(struct test_state *state, const struct test *test)
354 test->fn(state->out, state->in, 0);
355 check_test_output(state, test);
358 static void check(struct test_state *state, const struct test *test)
360 const struct regdef *def, *miss = 0;
365 for (def = test->regs; def->name; def++) {
366 reg = REG(in, def->i);
367 if (reg->f®F_LIVE) any = 1;
368 else if (!miss && !(def->f®F_OPT)) miss = def;
372 bail("register `%s' not set in test `%s'", def->name, test->name);
374 test->run(state, test);
376 for (def = test->regs; def->name; def++) {
377 reg = REG(in, def->i);
378 reg->f = 0; def->ty->release(®->v); def->ty->init(®->v);
382 int run_test_suite(unsigned nrout, unsigned nreg, size_t regsz,
383 const struct test *tests, FILE *fp)
385 struct linebuf buf = LINEBUF_INIT;
386 struct test_state state[1];
387 const struct test *test;
388 const struct regdef *def;
395 for (test = tests; test->name; test++)
396 for (def = test->regs; def->name; def++)
397 assert(def->i < nreg);
399 state->in = xmalloc(nreg*regsz);
400 state->out = xmalloc(nrout*regsz);
401 state->nrout = nrout;
403 state->regsz = regsz;
404 state->win = state->lose = 0;
408 while (!read_line(&buf, fp)) {
410 p = buf.p; n = strlen(buf.p);
412 while (isspace((unsigned char)*p)) p++;
413 if (*p == '#') continue;
414 if (!*p) { check(state, test); continue; }
417 while (*p && !isspace((unsigned char)*p)) p++;
420 if (!strcmp(q, "test")) {
421 if (!*p) bail("missing argument");
424 for (def = test->regs; def->name; def++) {
425 def->ty->release(®(in, def->i)->v);
426 if (def->i < state->nrout)
427 def->ty->release(®(out, def->i)->v);
430 for (test = tests; test->name; test++)
431 if (!strcmp(p, test->name)) goto found_test;
432 bail("unknown test `%s'", p);
434 for (def = test->regs; def->name; def++) {
435 reg = REG(in, def->i);
436 reg->f = 0; def->ty->init(®->v);
437 if (def->i < state->nrout) {
438 reg = REG(out, def->i);
439 reg->f = 0; def->ty->init(®->v);
445 if (!test) bail("no current test");
446 for (def = test->regs; def->name; def++)
447 if (!strcmp(q, def->name)) goto found_reg;
448 bail("unknown register `%s' in test `%s'", q, test->name);
450 reg = REG(in, def->i);
451 if (reg->f®F_LIVE) bail("register `%s' already set", def->name);
452 def->ty->parse(®->v, p); reg->f |= REGF_LIVE;
456 total = state->win + state->lose;
458 printf("PASSED all %d test%s\n", state->win, total == 1 ? "" : "s");
460 printf("FAILED %d of %d test%s\n", state->lose, total,
461 total == 1 ? "" : "s");
462 return state->lose ? 1 : 0;