2 * xdh-test.c: test harness elliptic-curve Diffie--Hellman
4 * (The implementations originally came with different test arrangements,
5 * with complicated external dependencies. This file replicates the original
6 * tests, but without the dependencies.)
9 * This file is Free Software. It was originally written for secnet.
11 * Copyright 2017 Mark Wooding
13 * You may redistribute secnet as a whole and/or modify it under the
14 * terms of the GNU General Public License as published by the Free
15 * Software Foundation; either version 3, or (at your option) any
18 * You may redistribute this file and/or modify it under the terms of
19 * the GNU General Public License as published by the Free Software
20 * Foundation; either version 2, or (at your option) any later
23 * This software is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this software; if not, see
30 * https://www.gnu.org/licenses/gpl.html.
50 #define GLUE(x, y) GLUE_(x, y)
51 #define GLUE_(x, y) x##y
52 #define FIELDOP(op) GLUE(FIELD, _##op)
54 #define f25519_FESZ 32u
55 #define fgoldi_FESZ 56u
57 /* Define some global variables we shouldn't need.
59 * Annoyingly, `secnet.h' declares static pointers and initializes them to
60 * point to some external variables. At `-O0', GCC doesn't optimize these
61 * away, so there's a link-time dependency on these variables. Define them
62 * here, so that `f25519.c' and `f448.c' can find them.
64 * (Later GCC has `-Og', which optimizes without making debugging a
65 * nightmare, but I'm not running that version here. Note that `serpent.c'
66 * doesn't have this problem because it defines its own word load and store
67 * operations to cope with its endian weirdness, whereas the field arithmetic
68 * uses `unaligned.h' which manages to include `secnet.h'.)
71 struct timeval tv_now_global
;
76 uint8_t fe
[FIELDOP(FESZ
)];
80 void (*parse
)(union reg
*r
, char *p
);
81 void (*dump
)(FILE *fp
, const union reg
*r
);
82 int (*eq
)(const union reg
*r
, const union reg
*rr
);
87 const struct regty
*ty
;
93 void (*fn
)(union reg
*out
, const union reg
*in
);
94 const struct regdef
*regs
;
99 static void bail(const char *msg
, ...)
103 fprintf(stderr
, "unexpected error (line %d): ", lno
);
104 vfprintf(stderr
, msg
, ap
);
110 static void parse_hex(uint8_t *b
, size_t sz
, char *p
)
112 size_t n
= strlen(p
);
116 if (n
%2) bail("bad hex (odd number of nibbles)");
117 else if (n
/2 != sz
) bail("bad hex (want %zu bytes, found %zu", sz
, n
/2);
119 for (i
= 0; i
< 2; i
++) {
120 if (!isxdigit((unsigned char)p
[i
]))
121 bail("bad hex digit `%c'", p
[i
]);
126 *b
++ = strtoul(bb
, 0, 16); sz
--;
130 static void dump_hex(FILE *fp
, const uint8_t *b
, size_t sz
)
131 { while (sz
--) fprintf(fp
, "%02x", *b
++); fputc('\n', fp
); }
133 static void parse_int(union reg
*r
, char *p
)
138 r
->i
= strtol(p
, &q
, 0);
139 if (*q
|| errno
) bail("bad integer `%s'", p
);
142 static void dump_int(FILE *fp
, const union reg
*r
)
143 { fprintf(fp
, "%ld\n", r
->i
); }
145 static int eq_int(const union reg
*r
, const union reg
*rr
)
146 { return (r
->i
== rr
->i
); }
148 static const struct regty regty_int
= { parse_int
, dump_int
, eq_int
};
150 static void parse_uint(union reg
*r
, char *p
)
155 r
->u
= strtoul(p
, &q
, 0);
156 if (*q
|| errno
) bail("bad integer `%s'", p
);
159 static void dump_uint(FILE *fp
, const union reg
*r
)
160 { fprintf(fp
, "%lu\n", r
->u
); }
162 static int eq_uint(const union reg
*r
, const union reg
*rr
)
163 { return (r
->u
== rr
->u
); }
165 static const struct regty regty_uint
= { parse_uint
, dump_uint
, eq_uint
};
167 static void parse_fe(union reg
*r
, char *p
)
168 { parse_hex(r
->fe
, sizeof(r
->fe
), p
); }
170 static void dump_fe(FILE *fp
, const union reg
*r
)
171 { dump_hex(fp
, r
->fe
, sizeof(r
->fe
)); }
173 static int eq_fe(const union reg
*r
, const union reg
*rr
)
174 { return (memcmp(r
->fe
, rr
->fe
, sizeof(r
->fe
)) == 0); }
176 static const struct regty regty_fe
= { parse_fe
, dump_fe
, eq_fe
};
179 RZ
, RXX
= RZ
, RYY
, NROUT
,
180 RX
= NROUT
, RY
, RA
= RY
, RK
= RY
, RM
, RN
= RM
, NREG
184 static void test_##op(union reg *out, const union reg *in) \
188 FIELDOP(load)(&x, in[RX].fe); \
189 FIELDOP(load)(&y, in[RY].fe); \
190 FIELDOP(op)(&z, &x, &y); \
191 FIELDOP(store)(out[RZ].fe, &z); \
195 static void test_##op(union reg *out, const union reg *in) \
199 FIELDOP(load)(&x, in[RX].fe); \
200 FIELDOP(op)(&z, &x); \
201 FIELDOP(store)(out[RZ].fe, &z); \
210 static void test_mulconst(union reg
*out
, const union reg
*in
)
214 FIELDOP(load
)(&x
, in
[RX
].fe
);
215 FIELDOP(mulconst
)(&z
, &x
, in
[RA
].i
);
216 FIELDOP(store
)(out
[RZ
].fe
, &z
);
219 static void test_condswap(union reg
*out
, const union reg
*in
)
223 FIELDOP(load
)(&x
, in
[RX
].fe
);
224 FIELDOP(load
)(&y
, in
[RY
].fe
);
225 FIELDOP(condswap
)(&x
, &y
, in
[RM
].u
);
226 FIELDOP(store
)(out
[RXX
].fe
, &x
);
227 FIELDOP(store
)(out
[RYY
].fe
, &y
);
230 static void test_xdh(union reg
*out
, const union reg
*in
)
231 { XDH(out
[RZ
].fe
, in
[RK
].fe
, in
[RX
].fe
); }
233 static void test_xdhmct(union reg
*out
, const union reg
*in
)
235 uint8_t b0
[FIELDOP(FESZ
)], b1
[FIELDOP(FESZ
)], *k
= b0
, *x
= b1
, *t
;
238 memcpy(b0
, in
[RK
].fe
, sizeof(b0
));
239 memcpy(b1
, in
[RX
].fe
, sizeof(b1
));
241 for (i
= 0; i
< n
; i
++) {
245 memcpy(out
[RZ
].fe
, k
, sizeof(b0
));
248 #define REG_X { "x", ®ty_fe, RX }
249 #define REG_Y { "y", ®ty_fe, RY }
250 #define REG_A { "a", ®ty_int, RA }
251 #define REG_M { "m", ®ty_uint, RM }
252 #define REG_XX { "xx", ®ty_fe, RXX }
253 #define REG_YY { "yy", ®ty_fe, RYY }
254 #define REG_Z { "z", ®ty_fe, RZ }
255 #define REG_BIGX { "X", ®ty_fe, RX }
256 #define REG_BIGZ { "Z", ®ty_fe, RZ }
257 #define REG_K { "k", ®ty_fe, RK }
258 #define REG_N { "n", ®ty_uint, RN }
259 #define REGLIST_END { 0 }
260 static const struct regdef
261 unop_regs
[] = { REG_X
, REG_Z
, REGLIST_END
},
262 binop_regs
[] = { REG_X
, REG_Y
, REG_Z
, REGLIST_END
},
263 mulconst_regs
[] = { REG_X
, REG_A
, REG_Z
, REGLIST_END
},
264 condswap_regs
[] = { REG_X
, REG_Y
, REG_M
, REG_XX
, REG_YY
, REGLIST_END
},
265 xdh_regs
[] = { REG_K
, REG_BIGX
, REG_BIGZ
, REGLIST_END
},
266 xdhmct_regs
[] = { REG_K
, REG_BIGX
, REG_N
, REG_BIGZ
, REGLIST_END
};
268 static const struct test tests
[] = {
269 { "add", test_add
, binop_regs
},
270 { "sub", test_sub
, binop_regs
},
271 { "condswap", test_condswap
, condswap_regs
},
272 { "mulconst", test_mulconst
, mulconst_regs
},
273 { "mul", test_mul
, binop_regs
},
274 { "sqr", test_sqr
, unop_regs
},
275 { "inv", test_inv
, unop_regs
},
276 { "xdh", test_xdh
, xdh_regs
},
277 { "xdh-mct", test_xdhmct
, xdhmct_regs
},
281 static int check(const struct test
*test
, unsigned iat
,
282 union reg
*out
, const union reg
*in
)
284 const struct regdef
*rdef
;
287 if (!iat
) return (0);
289 for (rdef
= test
->regs
; rdef
->name
; rdef
++)
290 if (!(iat
& (1 << rdef
->r
)))
291 bail("register `%s' not set", rdef
->name
);
293 for (rdef
= test
->regs
; rdef
->name
; rdef
++) {
294 if (rdef
->r
>= NROUT
) continue;
295 if (!rdef
->ty
->eq(&in
[rdef
->r
], &out
[rdef
->r
])) ok
= 0;
299 fprintf(stderr
, "failed `%s'\n", test
->name
);
300 for (rdef
= test
->regs
; rdef
->name
; rdef
++) {
301 if (rdef
->r
>= NROUT
) {
302 fprintf(stderr
, "\tinput `%s' = ", rdef
->name
);
303 rdef
->ty
->dump(stderr
, &in
[rdef
->r
]);
305 fprintf(stderr
, "\texpected `%s' = ", rdef
->name
);
306 rdef
->ty
->dump(stderr
, &in
[rdef
->r
]);
307 fprintf(stderr
, "\tcomputed `%s' = ", rdef
->name
);
308 rdef
->ty
->dump(stderr
, &out
[rdef
->r
]);
320 unsigned iat
= 0, rbit
;
321 const struct test
*test
= 0;
322 union reg in
[NREG
], out
[NROUT
];
323 const struct regdef
*rdef
;
327 while (fgets(linebuf
, sizeof(linebuf
), stdin
)) {
329 n
= strlen(linebuf
); assert(n
);
330 if (linebuf
[n
- 1] != '\n') bail("line too long");
333 while (isspace((unsigned char)*p
)) p
++;
334 if (*p
== '#') continue;
336 if (check(test
, iat
, out
, in
)) rc
= 2;
341 while (*p
&& !isspace((unsigned char)*p
)) p
++;
342 if (!*p
) bail("missing argument");
344 while (isspace((unsigned char)*p
)) p
++;
345 if (strcmp(q
, "test") == 0) {
346 if (check(test
, iat
, out
, in
)) rc
= 2;
348 for (test
= tests
; test
->name
; test
++)
349 if (strcmp(p
, test
->name
) == 0) goto found_test
;
350 bail("unknown test `%s'", p
);
354 if (!test
) bail("no test defined yet");
355 for (rdef
= test
->regs
; rdef
->name
; rdef
++)
356 if (strcmp(q
, rdef
->name
) == 0) goto found_reg
;
357 bail("unknown register `%s'", q
);
361 bail("register `%s' already set", rdef
->name
);
362 rdef
->ty
->parse(&in
[rdef
->r
], p
);
365 if (check(test
, iat
, out
, in
)) rc
= 2;