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;