| 1 | /* |
| 2 | * crypto-test.c: common test vector processing |
| 3 | */ |
| 4 | /* |
| 5 | * This file is Free Software. It was originally written for secnet. |
| 6 | * |
| 7 | * Copyright 2017, 2019 Mark Wooding |
| 8 | * |
| 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 |
| 12 | * later version. |
| 13 | * |
| 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 |
| 17 | * version. |
| 18 | * |
| 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. |
| 23 | * |
| 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. |
| 27 | */ |
| 28 | |
| 29 | #include <assert.h> |
| 30 | #include <errno.h> |
| 31 | #include <ctype.h> |
| 32 | #include <stdarg.h> |
| 33 | #include <stdio.h> |
| 34 | #include <stdlib.h> |
| 35 | #include <string.h> |
| 36 | |
| 37 | #include "secnet.h" |
| 38 | #include "util.h" |
| 39 | |
| 40 | #include "crypto-test.h" |
| 41 | |
| 42 | /*----- Utilities ---------------------------------------------------------*/ |
| 43 | |
| 44 | static void *xmalloc(size_t sz) |
| 45 | { |
| 46 | void *p; |
| 47 | |
| 48 | if (!sz) return 0; |
| 49 | p = malloc(sz); |
| 50 | if (!p) { |
| 51 | fprintf(stderr, "out of memory!\n"); |
| 52 | exit(2); |
| 53 | } |
| 54 | return p; |
| 55 | } |
| 56 | |
| 57 | static void *xrealloc(void *p, size_t sz) |
| 58 | { |
| 59 | void *q; |
| 60 | |
| 61 | if (!sz) { free(p); return 0; } |
| 62 | else if (!p) return xmalloc(sz); |
| 63 | q = realloc(p, sz); |
| 64 | if (!q) { |
| 65 | fprintf(stderr, "out of memory!\n"); |
| 66 | exit(2); |
| 67 | } |
| 68 | return q; |
| 69 | } |
| 70 | |
| 71 | static int lno; |
| 72 | |
| 73 | void bail(const char *msg, ...) |
| 74 | { |
| 75 | va_list ap; |
| 76 | va_start(ap, msg); |
| 77 | fprintf(stderr, "unexpected error (line %d): ", lno); |
| 78 | vfprintf(stderr, msg, ap); |
| 79 | va_end(ap); |
| 80 | fputc('\n', stderr); |
| 81 | exit(2); |
| 82 | } |
| 83 | |
| 84 | struct linebuf { |
| 85 | char *p; |
| 86 | size_t sz; |
| 87 | }; |
| 88 | #define LINEBUF_INIT { 0, 0 }; |
| 89 | |
| 90 | static int read_line(struct linebuf *b, FILE *fp) |
| 91 | { |
| 92 | size_t n = 0; |
| 93 | int ch; |
| 94 | |
| 95 | ch = getc(fp); if (ch == EOF) return EOF; |
| 96 | for (;;) { |
| 97 | if (n >= b->sz) { |
| 98 | b->sz = b->sz ? 2*b->sz : 64; |
| 99 | b->p = xrealloc(b->p, b->sz); |
| 100 | } |
| 101 | if (ch == EOF || ch == '\n') { b->p[n++] = 0; return 0; } |
| 102 | b->p[n++] = ch; |
| 103 | ch = getc(fp); |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | void parse_hex(uint8_t *b, size_t sz, char *p) |
| 108 | { |
| 109 | size_t n = strlen(p); |
| 110 | unsigned i; |
| 111 | char bb[3]; |
| 112 | |
| 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); |
| 115 | while (sz) { |
| 116 | for (i = 0; i < 2; i++) { |
| 117 | if (!isxdigit((unsigned char)p[i])) |
| 118 | bail("bad hex digit `%c'", p[i]); |
| 119 | bb[i] = p[i]; |
| 120 | } |
| 121 | bb[2] = 0; |
| 122 | p += 2; |
| 123 | *b++ = strtoul(bb, 0, 16); sz--; |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | void dump_hex(FILE *fp, const uint8_t *b, size_t sz) |
| 128 | { while (sz--) fprintf(fp, "%02x", *b++); fputc('\n', fp); } |
| 129 | |
| 130 | void trivial_regty_init(union regval *v) { ; } |
| 131 | void trivial_regty_release(union regval *v) { ; } |
| 132 | |
| 133 | /* Define some global variables we shouldn't need. |
| 134 | * |
| 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. |
| 139 | * |
| 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'.) |
| 145 | */ |
| 146 | uint64_t now_global; |
| 147 | struct timeval tv_now_global; |
| 148 | |
| 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(); } |
| 153 | |
| 154 | /* Bletch. util.c is a mess of layers. */ |
| 155 | int consttime_memeq(const void *s1in, const void *s2in, size_t n) |
| 156 | { |
| 157 | const uint8_t *s1=s1in, *s2=s2in; |
| 158 | register volatile uint8_t accumulator=0; |
| 159 | |
| 160 | while (n-- > 0) { |
| 161 | accumulator |= (*s1++ ^ *s2++); |
| 162 | } |
| 163 | accumulator |= accumulator >> 4; /* constant-time */ |
| 164 | accumulator |= accumulator >> 2; /* boolean canonicalisation */ |
| 165 | accumulator |= accumulator >> 1; |
| 166 | accumulator &= 1; |
| 167 | accumulator ^= 1; |
| 168 | return accumulator; |
| 169 | } |
| 170 | |
| 171 | /*----- Built-in types ----------------------------------------------------*/ |
| 172 | |
| 173 | /* Signed integer. */ |
| 174 | |
| 175 | static void parse_int(union regval *v, char *p) |
| 176 | { |
| 177 | char *q; |
| 178 | |
| 179 | errno = 0; |
| 180 | v->i = strtol(p, &q, 0); |
| 181 | if (*q || errno) bail("bad integer `%s'", p); |
| 182 | } |
| 183 | |
| 184 | static void dump_int(FILE *fp, const union regval *v) |
| 185 | { fprintf(fp, "%ld\n", v->i); } |
| 186 | |
| 187 | static int eq_int(const union regval *v0, const union regval *v1) |
| 188 | { return (v0->i == v1->i); } |
| 189 | |
| 190 | const struct regty regty_int = { |
| 191 | trivial_regty_init, |
| 192 | parse_int, |
| 193 | dump_int, |
| 194 | eq_int, |
| 195 | trivial_regty_release |
| 196 | }; |
| 197 | |
| 198 | /* Unsigned integer. */ |
| 199 | |
| 200 | static void parse_uint(union regval *v, char *p) |
| 201 | { |
| 202 | char *q; |
| 203 | |
| 204 | errno = 0; |
| 205 | v->u = strtoul(p, &q, 0); |
| 206 | if (*q || errno) bail("bad integer `%s'", p); |
| 207 | } |
| 208 | |
| 209 | static void dump_uint(FILE *fp, const union regval *v) |
| 210 | { fprintf(fp, "%lu\n", v->u); } |
| 211 | |
| 212 | static int eq_uint(const union regval *v0, const union regval *v1) |
| 213 | { return (v0->u == v1->u); } |
| 214 | |
| 215 | const struct regty regty_uint = { |
| 216 | trivial_regty_init, |
| 217 | parse_uint, |
| 218 | dump_uint, |
| 219 | eq_uint, |
| 220 | trivial_regty_release |
| 221 | }; |
| 222 | |
| 223 | /* Byte string, as hex. */ |
| 224 | |
| 225 | void allocate_bytes(union regval *v, size_t sz) |
| 226 | { v->bytes.p = xmalloc(sz); v->bytes.sz = sz; } |
| 227 | |
| 228 | static void init_bytes(union regval *v) { v->bytes.p = 0; v->bytes.sz = 0; } |
| 229 | |
| 230 | static void parse_bytes(union regval *v, char *p) |
| 231 | { |
| 232 | size_t n = strlen(p); |
| 233 | |
| 234 | allocate_bytes(v, n/2); |
| 235 | parse_hex(v->bytes.p, v->bytes.sz, p); |
| 236 | } |
| 237 | |
| 238 | static void dump_bytes(FILE *fp, const union regval *v) |
| 239 | { dump_hex(fp, v->bytes.p, v->bytes.sz); } |
| 240 | |
| 241 | static int eq_bytes(const union regval *v0, const union regval *v1) |
| 242 | { |
| 243 | return v0->bytes.sz == v1->bytes.sz && |
| 244 | !memcmp(v0->bytes.p, v1->bytes.p, v0->bytes.sz); |
| 245 | } |
| 246 | |
| 247 | static void release_bytes(union regval *v) { free(v->bytes.p); } |
| 248 | |
| 249 | const struct regty regty_bytes = { |
| 250 | init_bytes, |
| 251 | parse_bytes, |
| 252 | dump_bytes, |
| 253 | eq_bytes, |
| 254 | release_bytes |
| 255 | }; |
| 256 | |
| 257 | /* Text strings. Not really intended as an output type. */ |
| 258 | |
| 259 | void allocate_string(union regval *v, size_t sz) |
| 260 | { v->str.p = xmalloc(sz + 1); v->str.sz = sz; } |
| 261 | |
| 262 | static void init_string(union regval *v) { v->str.p = 0; v->str.sz = 0; } |
| 263 | |
| 264 | static void parse_string(union regval *v, char *p) |
| 265 | { |
| 266 | size_t n = strlen(p); |
| 267 | |
| 268 | allocate_string(v, n); |
| 269 | memcpy(v->str.p, p, n + 1); |
| 270 | } |
| 271 | |
| 272 | static void dump_string(FILE *fp, const union regval *v) |
| 273 | { |
| 274 | if (v->str.p) fprintf(fp, "`%s'\n", v->str.p); |
| 275 | else fputs("nil\n", fp); |
| 276 | } |
| 277 | |
| 278 | static int eq_string(const union regval *v0, const union regval *v1) |
| 279 | { |
| 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); |
| 282 | } |
| 283 | |
| 284 | static void release_string(union regval *v) { free(v->str.p); } |
| 285 | |
| 286 | const struct regty regty_string = { |
| 287 | init_string, |
| 288 | parse_string, |
| 289 | dump_string, |
| 290 | eq_string, |
| 291 | release_string |
| 292 | }; |
| 293 | |
| 294 | /*----- Core test machinery -----------------------------------------------*/ |
| 295 | |
| 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 |
| 302 | * data. |
| 303 | */ |
| 304 | |
| 305 | #define REG(rvec, i) \ |
| 306 | ((struct reg *)((unsigned char *)state->rvec + (i)*state->regsz)) |
| 307 | |
| 308 | void check_test_output(struct test_state *state, const struct test *test) |
| 309 | { |
| 310 | const struct regdef *def; |
| 311 | struct reg *reg, *in, *out; |
| 312 | int ok = 1; |
| 313 | int match; |
| 314 | |
| 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; |
| 319 | } |
| 320 | if (ok) |
| 321 | state->win++; |
| 322 | else { |
| 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); |
| 330 | } else { |
| 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); |
| 336 | if (!match) { |
| 337 | printf("\tcomputed `%s' = ", def->name); |
| 338 | def->ty->dump(stdout, &out->v); |
| 339 | } |
| 340 | } |
| 341 | } |
| 342 | state->lose++; |
| 343 | } |
| 344 | |
| 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); |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | void run_test(struct test_state *state, const struct test *test) |
| 353 | { |
| 354 | test->fn(state->out, state->in, 0); |
| 355 | check_test_output(state, test); |
| 356 | } |
| 357 | |
| 358 | static void check(struct test_state *state, const struct test *test) |
| 359 | { |
| 360 | const struct regdef *def, *miss = 0; |
| 361 | struct reg *reg; |
| 362 | int any = 0; |
| 363 | |
| 364 | if (!test) return; |
| 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; |
| 369 | } |
| 370 | if (!any) return; |
| 371 | if (miss) |
| 372 | bail("register `%s' not set in test `%s'", def->name, test->name); |
| 373 | |
| 374 | test->run(state, test); |
| 375 | |
| 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); |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | int run_test_suite(unsigned nrout, unsigned nreg, size_t regsz, |
| 383 | const struct test *tests, FILE *fp) |
| 384 | { |
| 385 | struct linebuf buf = LINEBUF_INIT; |
| 386 | struct test_state state[1]; |
| 387 | const struct test *test; |
| 388 | const struct regdef *def; |
| 389 | struct reg *reg; |
| 390 | char *p; |
| 391 | const char *q; |
| 392 | int total; |
| 393 | size_t n; |
| 394 | |
| 395 | for (test = tests; test->name; test++) |
| 396 | for (def = test->regs; def->name; def++) |
| 397 | assert(def->i < nreg); |
| 398 | |
| 399 | state->in = xmalloc(nreg*regsz); |
| 400 | state->out = xmalloc(nrout*regsz); |
| 401 | state->nrout = nrout; |
| 402 | state->nreg = nreg; |
| 403 | state->regsz = regsz; |
| 404 | state->win = state->lose = 0; |
| 405 | |
| 406 | test = 0; |
| 407 | lno = 0; |
| 408 | while (!read_line(&buf, fp)) { |
| 409 | lno++; |
| 410 | p = buf.p; n = strlen(buf.p); |
| 411 | |
| 412 | while (isspace((unsigned char)*p)) p++; |
| 413 | if (*p == '#') continue; |
| 414 | if (!*p) { check(state, test); continue; } |
| 415 | |
| 416 | q = p; |
| 417 | while (*p && !isspace((unsigned char)*p)) p++; |
| 418 | if (*p) *p++ = 0; |
| 419 | |
| 420 | if (!strcmp(q, "test")) { |
| 421 | if (!*p) bail("missing argument"); |
| 422 | check(state, test); |
| 423 | if (test) { |
| 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); |
| 428 | } |
| 429 | } |
| 430 | for (test = tests; test->name; test++) |
| 431 | if (!strcmp(p, test->name)) goto found_test; |
| 432 | bail("unknown test `%s'", p); |
| 433 | found_test: |
| 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); |
| 440 | } |
| 441 | } |
| 442 | continue; |
| 443 | } |
| 444 | |
| 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); |
| 449 | found_reg: |
| 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; |
| 453 | } |
| 454 | check(state, test); |
| 455 | |
| 456 | total = state->win + state->lose; |
| 457 | if (!state->lose) |
| 458 | printf("PASSED all %d test%s\n", state->win, total == 1 ? "" : "s"); |
| 459 | else |
| 460 | printf("FAILED %d of %d test%s\n", state->lose, total, |
| 461 | total == 1 ? "" : "s"); |
| 462 | return state->lose ? 1 : 0; |
| 463 | } |