--- /dev/null
+/*
+ * crypto-test.c: common test vector processing
+ */
+/*
+ * This file is Free Software. It was originally written for secnet.
+ *
+ * Copyright 2017, 2019 Mark Wooding
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "secnet.h"
+#include "util.h"
+
+#include "crypto-test.h"
+
+/*----- Utilities ---------------------------------------------------------*/
+
+static void *xmalloc(size_t sz)
+{
+ void *p;
+
+ if (!sz) return 0;
+ p = malloc(sz);
+ if (!p) {
+ fprintf(stderr, "out of memory!\n");
+ exit(2);
+ }
+ return p;
+}
+
+static void *xrealloc(void *p, size_t sz)
+{
+ void *q;
+
+ if (!sz) { free(p); return 0; }
+ else if (!p) return xmalloc(sz);
+ q = realloc(p, sz);
+ if (!q) {
+ fprintf(stderr, "out of memory!\n");
+ exit(2);
+ }
+ return q;
+}
+
+static int lno;
+
+void bail(const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ fprintf(stderr, "unexpected error (line %d): ", lno);
+ vfprintf(stderr, msg, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+ exit(2);
+}
+
+struct linebuf {
+ char *p;
+ size_t sz;
+};
+#define LINEBUF_INIT { 0, 0 };
+
+static int read_line(struct linebuf *b, FILE *fp)
+{
+ size_t n = 0;
+ int ch;
+
+ ch = getc(fp); if (ch == EOF) return EOF;
+ for (;;) {
+ if (n >= b->sz) {
+ b->sz = b->sz ? 2*b->sz : 64;
+ b->p = xrealloc(b->p, b->sz);
+ }
+ if (ch == EOF || ch == '\n') { b->p[n++] = 0; return 0; }
+ b->p[n++] = ch;
+ ch = getc(fp);
+ }
+}
+
+void parse_hex(uint8_t *b, size_t sz, char *p)
+{
+ size_t n = strlen(p);
+ unsigned i;
+ char bb[3];
+
+ if (n%2) bail("bad hex (odd number of nibbles)");
+ else if (n/2 != sz) bail("bad hex (want %zu bytes, found %zu)", sz, n/2);
+ while (sz) {
+ for (i = 0; i < 2; i++) {
+ if (!isxdigit((unsigned char)p[i]))
+ bail("bad hex digit `%c'", p[i]);
+ bb[i] = p[i];
+ }
+ bb[2] = 0;
+ p += 2;
+ *b++ = strtoul(bb, 0, 16); sz--;
+ }
+}
+
+void dump_hex(FILE *fp, const uint8_t *b, size_t sz)
+ { while (sz--) fprintf(fp, "%02x", *b++); fputc('\n', fp); }
+
+void trivial_regty_init(union regval *v) { ; }
+void trivial_regty_release(union regval *v) { ; }
+
+/* Define some global variables we shouldn't need.
+ *
+ * Annoyingly, `secnet.h' declares static pointers and initializes them to
+ * point to some external variables. At `-O0', GCC doesn't optimize these
+ * away, so there's a link-time dependency on these variables. Define them
+ * here, so that `f25519.c' and `f448.c' can find them.
+ *
+ * (Later GCC has `-Og', which optimizes without making debugging a
+ * nightmare, but I'm not running that version here. Note that `serpent.c'
+ * doesn't have this problem because it defines its own word load and store
+ * operations to cope with its endian weirdness, whereas the field arithmetic
+ * uses `unaligned.h' which manages to include `secnet.h'.)
+ */
+uint64_t now_global;
+struct timeval tv_now_global;
+
+/* Bletch. util.c is a mess of layers. */
+int consttime_memeq(const void *s1in, const void *s2in, size_t n)
+{
+ const uint8_t *s1=s1in, *s2=s2in;
+ register volatile uint8_t accumulator=0;
+
+ while (n-- > 0) {
+ accumulator |= (*s1++ ^ *s2++);
+ }
+ accumulator |= accumulator >> 4; /* constant-time */
+ accumulator |= accumulator >> 2; /* boolean canonicalisation */
+ accumulator |= accumulator >> 1;
+ accumulator &= 1;
+ accumulator ^= 1;
+ return accumulator;
+}
+
+/*----- Built-in types ----------------------------------------------------*/
+
+/* Signed integer. */
+
+static void parse_int(union regval *v, char *p)
+{
+ char *q;
+
+ errno = 0;
+ v->i = strtol(p, &q, 0);
+ if (*q || errno) bail("bad integer `%s'", p);
+}
+
+static void dump_int(FILE *fp, const union regval *v)
+ { fprintf(fp, "%ld\n", v->i); }
+
+static int eq_int(const union regval *v0, const union regval *v1)
+ { return (v0->i == v1->i); }
+
+const struct regty regty_int = {
+ trivial_regty_init,
+ parse_int,
+ dump_int,
+ eq_int,
+ trivial_regty_release
+};
+
+/* Unsigned integer. */
+
+static void parse_uint(union regval *v, char *p)
+{
+ char *q;
+
+ errno = 0;
+ v->u = strtoul(p, &q, 0);
+ if (*q || errno) bail("bad integer `%s'", p);
+}
+
+static void dump_uint(FILE *fp, const union regval *v)
+ { fprintf(fp, "%lu\n", v->u); }
+
+static int eq_uint(const union regval *v0, const union regval *v1)
+ { return (v0->u == v1->u); }
+
+const struct regty regty_uint = {
+ trivial_regty_init,
+ parse_uint,
+ dump_uint,
+ eq_uint,
+ trivial_regty_release
+};
+
+/* Byte string, as hex. */
+
+void allocate_bytes(union regval *v, size_t sz)
+ { v->bytes.p = xmalloc(sz); v->bytes.sz = sz; }
+
+static void init_bytes(union regval *v) { v->bytes.p = 0; v->bytes.sz = 0; }
+
+static void parse_bytes(union regval *v, char *p)
+{
+ size_t n = strlen(p);
+
+ allocate_bytes(v, n/2);
+ parse_hex(v->bytes.p, v->bytes.sz, p);
+}
+
+static void dump_bytes(FILE *fp, const union regval *v)
+ { dump_hex(fp, v->bytes.p, v->bytes.sz); }
+
+static int eq_bytes(const union regval *v0, const union regval *v1)
+{
+ return v0->bytes.sz == v1->bytes.sz &&
+ !memcmp(v0->bytes.p, v1->bytes.p, v0->bytes.sz);
+}
+
+static void release_bytes(union regval *v) { free(v->bytes.p); }
+
+const struct regty regty_bytes = {
+ init_bytes,
+ parse_bytes,
+ dump_bytes,
+ eq_bytes,
+ release_bytes
+};
+
+/*----- Core test machinery -----------------------------------------------*/
+
+/* Say that a register is `reset' by releasing and then re-initializing it.
+ * While there is a current test, all of that test's registers are
+ * initialized. The input registers are reset at the end of `check', ready
+ * for the next test to load new values. The output registers are reset at
+ * the end of `check_test_output', so that a test runner can run a test
+ * multiple times against the same test input, but with different context
+ * data.
+ */
+
+#define REG(rvec, i) \
+ ((struct reg *)((unsigned char *)state->rvec + (i)*state->regsz))
+
+void check_test_output(struct test_state *state, const struct test *test)
+{
+ const struct regdef *def;
+ struct reg *reg, *in, *out;
+ int ok = 1;
+ int match;
+
+ for (def = test->regs; def->name; def++) {
+ if (def->i >= state->nrout) continue;
+ in = REG(in, def->i); out = REG(out, def->i);
+ if (!def->ty->eq(&in->v, &out->v)) ok = 0;
+ }
+ if (ok)
+ state->win++;
+ else {
+ printf("failed test `%s'\n", test->name);
+ for (def = test->regs; def->name; def++) {
+ in = REG(in, def->i);
+ if (def->i >= state->nrout) {
+ printf("\t input `%s' = ", def->name);
+ def->ty->dump(stdout, &in->v);
+ } else {
+ out = REG(out, def->i);
+ match = def->ty->eq(&in->v, &out->v);
+ printf("\t%s `%s' = ",
+ match ? " output" : "expected", def->name);
+ def->ty->dump(stdout, &in->v);
+ if (!match) {
+ printf("\tcomputed `%s' = ", def->name);
+ def->ty->dump(stdout, &out->v);
+ }
+ }
+ }
+ state->lose++;
+ }
+
+ for (def = test->regs; def->name; def++) {
+ if (def->i >= state->nrout) continue;
+ reg = REG(out, def->i);
+ def->ty->release(®->v); def->ty->init(®->v);
+ }
+}
+
+void run_test(struct test_state *state, const struct test *test)
+{
+ test->fn(state->out, state->in, 0);
+ check_test_output(state, test);
+}
+
+static void check(struct test_state *state, const struct test *test)
+{
+ const struct regdef *def, *miss = 0;
+ struct reg *reg;
+ int any = 0;
+
+ if (!test) return;
+ for (def = test->regs; def->name; def++) {
+ reg = REG(in, def->i);
+ if (reg->f®F_LIVE) any = 1;
+ else if (!miss && !(def->f®F_OPT)) miss = def;
+ }
+ if (!any) return;
+ if (miss)
+ bail("register `%s' not set in test `%s'", def->name, test->name);
+
+ test->run(state, test);
+
+ for (def = test->regs; def->name; def++) {
+ reg = REG(in, def->i);
+ reg->f = 0; def->ty->release(®->v); def->ty->init(®->v);
+ }
+}
+
+int run_test_suite(unsigned nrout, unsigned nreg, size_t regsz,
+ const struct test *tests, FILE *fp)
+{
+ struct linebuf buf = LINEBUF_INIT;
+ struct test_state state[1];
+ const struct test *test;
+ const struct regdef *def;
+ struct reg *reg;
+ char *p;
+ const char *q;
+ int total;
+ size_t n;
+
+ for (test = tests; test->name; test++)
+ for (def = test->regs; def->name; def++)
+ assert(def->i < nreg);
+
+ state->in = xmalloc(nreg*regsz);
+ state->out = xmalloc(nrout*regsz);
+ state->nrout = nrout;
+ state->nreg = nreg;
+ state->regsz = regsz;
+ state->win = state->lose = 0;
+
+ test = 0;
+ lno = 0;
+ while (!read_line(&buf, fp)) {
+ lno++;
+ p = buf.p; n = strlen(buf.p);
+
+ while (isspace((unsigned char)*p)) p++;
+ if (*p == '#') continue;
+ if (!*p) { check(state, test); continue; }
+
+ q = p;
+ while (*p && !isspace((unsigned char)*p)) p++;
+ if (*p) *p++ = 0;
+
+ if (!strcmp(q, "test")) {
+ if (!*p) bail("missing argument");
+ check(state, test);
+ if (test) {
+ for (def = test->regs; def->name; def++) {
+ def->ty->release(®(in, def->i)->v);
+ if (def->i < state->nrout)
+ def->ty->release(®(out, def->i)->v);
+ }
+ }
+ for (test = tests; test->name; test++)
+ if (!strcmp(p, test->name)) goto found_test;
+ bail("unknown test `%s'", p);
+ found_test:
+ for (def = test->regs; def->name; def++) {
+ reg = REG(in, def->i);
+ reg->f = 0; def->ty->init(®->v);
+ if (def->i < state->nrout) {
+ reg = REG(out, def->i);
+ reg->f = 0; def->ty->init(®->v);
+ }
+ }
+ continue;
+ }
+
+ if (!test) bail("no current test");
+ for (def = test->regs; def->name; def++)
+ if (!strcmp(q, def->name)) goto found_reg;
+ bail("unknown register `%s' in test `%s'", q, test->name);
+ found_reg:
+ reg = REG(in, def->i);
+ if (reg->f®F_LIVE) bail("register `%s' already set", def->name);
+ def->ty->parse(®->v, p); reg->f |= REGF_LIVE;
+ }
+ check(state, test);
+
+ total = state->win + state->lose;
+ if (!state->lose)
+ printf("PASSED all %d test%s\n", state->win, total == 1 ? "" : "s");
+ else
+ printf("FAILED %d of %d test%s\n", state->lose, total,
+ total == 1 ? "" : "s");
+ return state->lose ? 1 : 0;
+}
--- /dev/null
+/*
+ * crypto-test.h: common test vector processing
+ */
+/*
+ * This file is Free Software. It was originally written for secnet.
+ *
+ * Copyright 2017, 2019 Mark Wooding
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef crypto_test_h
+#define crypto_test_h
+
+/* Basic model.
+ *
+ * There is a collection of `registers', each of which can store a value.
+ * Some registers are designated as `input': their values are set while
+ * reading the test vector. Other registers are designated as `output': the
+ * test function is expected to calculate their values, which are then
+ * compared against final values supplied by the test vector. Any
+ * discrepancies are reported.
+ *
+ * While a test suite is running, there is a single vector of registers, and
+ * registers are identified by index, starting from zero. The number of
+ * registers, `nreg', and a threshold `nrout', are defined by the test suite:
+ * registers with index less than `nrout' are for output; other registers up
+ * to, but not including, `nreg' are for input. Finally, the test suite
+ * defines the size of a register. The common test machinery treats
+ * registers as entirely opaque, acting on them only through their defined
+ * types.
+ *
+ * Each kind of test defines a register mapping, which assigns types and
+ * (textual) names to some subset of the registers. A register can be marked
+ * optional; by default, the test vector parser will report an error if a
+ * defined register is not assigned a value.
+ *
+ * The register type is responsible for handling the register on behalf of
+ * the common code. (Test functions can have built-in knowledge of which
+ * registers have which types, and can manipulate registers directly, of
+ * course.)
+ *
+ * Finally, each test defines a test runner, which is responsible for
+ * invoking the test function and checking that the output registers are
+ * correct. There is a generic test runner, but some tests might benefit
+ * from special arrangements.
+ */
+
+union regval {
+ long i; /* signed integer */
+ unsigned long u; /* unsigned integer */
+ struct { void *p; size_t sz; } bytes; /* buffer of bytes */
+#ifdef REG_MEMBERS
+ REG_MEMBERS /* your members here */
+#endif
+};
+
+struct reg {
+ unsigned f; /* flags */
+#define REGF_LIVE 1u /* input register has a value */
+ union regval v; /* register value */
+};
+
+struct regty {
+ void (*init)(union regval *v); /* set up raw memory */
+ void (*parse)(union regval *v, char *p); /* parse text input as value */
+ void (*dump)(FILE *fp, const union regval *v); /* dump value as text */
+ int (*eq)(const union regval *v0, const union regval *v1); /* equal? */
+ void (*release)(union regval *v); /* release any resources */
+};
+
+struct regdef {
+ const char *name; /* register name (for input files) */
+ unsigned i; /* register index */
+ const struct regty *ty; /* register type descriptor */
+ unsigned f; /* flags */
+#define REGF_OPT 1u /* (input) register is optional */
+};
+#define REGLIST_END { 0 }
+
+struct test_state {
+ struct reg *in, *out; /* vectors of registers */
+ unsigned nrout; /* number of output registers */
+ unsigned nreg; /* total number of registers */
+ size_t regsz; /* size of an individual register */
+ int win, lose; /* number of tests passed/failed */
+};
+
+struct test {
+ const char *name; /* name of the test */
+ void (*run)(struct test_state *state, const struct test *test);
+ /* test runner (`run_test') */
+ const struct regdef *regs; /* register definitions */
+ void (*fn)(struct reg *out, const struct reg *in, void *ctx);
+ /* test function */
+};
+
+/* Utility functions. */
+extern NORETURN(bail(const char *msg, ...))
+ FORMAT(printf, 1, 2);
+extern void parse_hex(uint8_t *b, size_t sz, char *p);
+extern void dump_hex(FILE *fp, const uint8_t *b, size_t sz);
+extern void trivial_regty_init(union regval *v);
+extern void trivial_regty_release(union regval *v);
+extern void allocate_bytes(union regval *v, size_t sz);
+
+/* Built-in register types. */
+extern const struct regty
+ regty_int,
+ regty_uint,
+ regty_bytes;
+
+/* Running tests. */
+extern void check_test_output(struct test_state *state,
+ const struct test *test);
+extern void run_test(struct test_state *state, const struct test *test);
+extern int run_test_suite(unsigned nrout, unsigned nreg, size_t regsz,
+ const struct test *tests, FILE *fp);
+
+#endif
--- /dev/null
+/*
+ * ec-field-test.c: test harness for elliptic-curve field arithmetic
+ *
+ * (The implementations originally came with different test arrangements,
+ * with complicated external dependencies. This file replicates the original
+ * tests, but without the dependencies.)
+ */
+/*
+ * This file is Free Software. It was originally written for secnet.
+ *
+ * Copyright 2017 Mark Wooding
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include <stdio.h>
+
+#include "secnet.h"
+
+#include "f25519.h"
+#include "fgoldi.h"
+
+#define f25519_FESZ 32u
+#define fgoldi_FESZ 56u
+
+#define GLUE(x, y) GLUE_(x, y)
+#define GLUE_(x, y) x##y
+#define FIELDOP(op) GLUE(FIELD, _##op)
+
+#define REG_MEMBERS \
+ uint8_t fe[FIELDOP(FESZ)];
+#include "crypto-test.h"
+
+enum {
+ RZ, RZ0 = RZ, RZ1, RXX = RZ0, RYY = RZ1, NROUT,
+ RM = NROUT, RI = RM, RN = RM,
+ RU, RV, RW, RX, RY, RA,
+ RV0 = RU, RV31 = RV0 + 31,
+ NREG
+};
+
+static void parse_fe(union regval *v, char *p)
+{
+ size_t n = strlen(p);
+ size_t sz = sizeof(v->fe);
+
+ if (!*p)
+ memset(v->fe, 0xff, sizeof(v->fe));
+ else {
+ if (sz > n/2) sz = n/2;
+ parse_hex(v->fe, sz, p); memset(v->fe + sz, 0, sizeof(v->fe) - sz);
+ }
+}
+
+static void dump_fe(FILE *fp, const union regval *v)
+ { dump_hex(fp, v->fe, sizeof(v->fe)); }
+
+static int eq_fe(const union regval *v0, const union regval *v1)
+ { return (memcmp(v0->fe, v1->fe, sizeof(v0->fe)) == 0); }
+
+static const struct regty regty_fe = {
+ trivial_regty_init,
+ parse_fe,
+ dump_fe,
+ eq_fe,
+ trivial_regty_release
+};
+
+#define BINOP(op) \
+ static void test_##op(struct reg *out, \
+ const struct reg *in, void *ctx) \
+ { \
+ FIELD x, y, z; \
+ \
+ FIELDOP(load)(&x, in[RX].v.fe); \
+ FIELDOP(load)(&y, in[RY].v.fe); \
+ FIELDOP(op)(&z, &x, &y); \
+ FIELDOP(store)(out[RZ].v.fe, &z); \
+ }
+
+#define UNOP(op) \
+ static void test_##op(struct reg *out, \
+ const struct reg *in, void *ctx) \
+ { \
+ FIELD x, z; \
+ \
+ FIELDOP(load)(&x, in[RX].v.fe); \
+ FIELDOP(op)(&z, &x); \
+ FIELDOP(store)(out[RZ].v.fe, &z); \
+ }
+
+BINOP(add)
+BINOP(sub)
+BINOP(mul)
+UNOP(neg)
+UNOP(sqr)
+UNOP(inv)
+
+static void test_condneg(struct reg *out, const struct reg *in, void *ctx)
+{
+ FIELD x, z;
+
+ FIELDOP(load)(&x, in[RX].v.fe);
+ FIELDOP(condneg)(&z, &x, in[RM].v.u);
+ FIELDOP(store)(out[RZ].v.fe, &z);
+}
+
+static void test_mulconst(struct reg *out, const struct reg *in, void *ctx)
+{
+ FIELD x, z;
+
+ FIELDOP(load)(&x, in[RX].v.fe);
+ FIELDOP(mulconst)(&z, &x, in[RA].v.i);
+ FIELDOP(store)(out[RZ].v.fe, &z);
+}
+
+static void test_condswap(struct reg *out, const struct reg *in, void *ctx)
+{
+ FIELD x, y;
+
+ FIELDOP(load)(&x, in[RX].v.fe);
+ FIELDOP(load)(&y, in[RY].v.fe);
+ FIELDOP(condswap)(&x, &y, in[RM].v.u);
+ FIELDOP(store)(out[RXX].v.fe, &x);
+ FIELDOP(store)(out[RYY].v.fe, &y);
+}
+
+static void test_pick2(struct reg *out, const struct reg *in, void *ctx)
+{
+ FIELD x, y, z;
+
+ FIELDOP(load)(&x, in[RX].v.fe);
+ FIELDOP(load)(&y, in[RY].v.fe);
+ FIELDOP(pick2)(&z, &x, &y, in[RM].v.u);
+ FIELDOP(store)(out[RZ].v.fe, &z);
+}
+
+static void test_pickn(struct reg *out, const struct reg *in, void *ctx)
+{
+ FIELD v[32], z;
+ unsigned i;
+
+ for (i = 0; in[RV0 + i].f®F_LIVE; i++)
+ FIELDOP(load)(&v[i], in[RV0 + i].v.fe);
+ FIELDOP(pickn)(&z, v, i, in[RI].v.u);
+ FIELDOP(store)(out[RZ].v.fe, &z);
+}
+
+static void test_quosqrt(struct reg *out, const struct reg *in, void *ctx)
+{
+ FIELD x, y, z;
+
+ FIELDOP(load)(&x, in[RX].v.fe);
+ FIELDOP(load)(&y, in[RY].v.fe);
+ if (FIELDOP(quosqrt)(&z, &x, &y))
+ memset(out[RZ0].v.fe, 0xff, sizeof(out[RZ].v.fe));
+ else
+ FIELDOP(store)(out[RZ].v.fe, &z);
+}
+
+static void run_quosqrt(struct test_state *state, const struct test *test)
+{
+ test->fn(state->out, state->in, 0);
+
+ /* ..._quosqrt returns an arbitrary square root. The test vector
+ * contains both. We win if we match either.
+ */
+ if (eq_fe(&state->in[RZ1].v, &state->out[RZ].v))
+ state->out[RZ0].v = state->in[RZ0].v;
+ state->out[RZ1].v = state->in[RZ1].v;
+ check_test_output(state, test);
+}
+
+static void test_sub_mulc_add_sub_mul(struct reg *out,
+ const struct reg *in, void *ctx)
+{
+ FIELD u, v, w, x, y, z;
+
+ FIELDOP(load)(&u, in[RU].v.fe);
+ FIELDOP(load)(&v, in[RV].v.fe);
+ FIELDOP(load)(&w, in[RW].v.fe);
+ FIELDOP(load)(&x, in[RX].v.fe);
+ FIELDOP(load)(&y, in[RY].v.fe);
+
+ FIELDOP(sub)(&z, &u, &v);
+ FIELDOP(mulconst)(&z, &z, in[RA].v.i);
+ FIELDOP(add)(&z, &z, &w);
+ FIELDOP(sub)(&x, &x, &y);
+ FIELDOP(mul)(&z, &z, &x);
+ FIELDOP(store)(out[RZ].v.fe, &z);
+}
+
+#define REG_U { "u", RU, ®ty_fe, 0 }
+#define REG_V { "v", RV, ®ty_fe, 0 }
+#define REG_W { "w", RW, ®ty_fe, 0 }
+#define REG_X { "x", RX, ®ty_fe, 0 }
+#define REG_Y { "y", RY, ®ty_fe, 0 }
+#define REG_A { "a", RA, ®ty_int, 0 }
+#define REG_M { "m", RM, ®ty_uint, 0 }
+#define REG_I { "i", RI, ®ty_uint, 0 }
+#define REG_XX { "xx", RXX, ®ty_fe, 0 }
+#define REG_YY { "yy", RYY, ®ty_fe, 0 }
+#define REG_Z { "z", RZ, ®ty_fe, 0 }
+#define REG_Z0 { "z0", RZ0, ®ty_fe, 0 }
+#define REG_Z1 { "z1", RZ1, ®ty_fe, 0 }
+#define REG_BIGY { "Y", RY, ®ty_fe, 0 }
+#define REG_BIGZ { "Z", RZ, ®ty_fe, 0 }
+#define REG_N { "n", RN, ®ty_uint, 0 }
+#define REG_Vi(i) { "v[" # i "]", RV0 + i, ®ty_fe, REGF_OPT }
+#define REG_VV \
+ REG_Vi( 0), REG_Vi( 1), REG_Vi( 2), REG_Vi( 3), \
+ REG_Vi( 4), REG_Vi( 5), REG_Vi( 6), REG_Vi( 7), \
+ REG_Vi( 8), REG_Vi( 9), REG_Vi(10), REG_Vi(11), \
+ REG_Vi(12), REG_Vi(13), REG_Vi(14), REG_Vi(15), \
+ REG_Vi(16), REG_Vi(17), REG_Vi(18), REG_Vi(19), \
+ REG_Vi(20), REG_Vi(21), REG_Vi(22), REG_Vi(23), \
+ REG_Vi(24), REG_Vi(25), REG_Vi(26), REG_Vi(27), \
+ REG_Vi(28), REG_Vi(29), REG_Vi(30), REG_Vi(31)
+static const struct regdef
+ unop_regs[] = { REG_X, REG_Z, REGLIST_END },
+ binop_regs[] = { REG_X, REG_Y, REG_Z, REGLIST_END },
+ condneg_regs[] = { REG_X, REG_M, REG_Z, REGLIST_END },
+ mulconst_regs[] = { REG_X, REG_A, REG_Z, REGLIST_END },
+ pick2_regs[] = { REG_X, REG_Y, REG_M, REG_Z, REGLIST_END },
+ pickn_regs[] = { REG_VV, REG_I, REG_Z, REGLIST_END },
+ condswap_regs[] = { REG_X, REG_Y, REG_M, REG_XX, REG_YY, REGLIST_END },
+ quosqrt_regs[] = { REG_X, REG_Y, REG_Z0, REG_Z1, REGLIST_END },
+ sub_mulc_add_sub_mul_regs[] =
+ { REG_U, REG_V, REG_A, REG_W, REG_X, REG_Y, REG_Z, REGLIST_END };
+
+static const struct test tests[] = {
+ { "add", run_test, binop_regs, test_add },
+ { "sub", run_test, binop_regs, test_sub },
+ { "neg", run_test, unop_regs, test_neg },
+ { "condneg", run_test, condneg_regs, test_condneg },
+ { "condswap", run_test, condswap_regs, test_condswap },
+ { "mulconst", run_test, mulconst_regs, test_mulconst },
+ { "mul", run_test, binop_regs, test_mul },
+ { "sqr", run_test, unop_regs, test_sqr },
+ { "inv", run_test, unop_regs, test_inv },
+ { "pick2", run_test, pick2_regs, test_pick2 },
+ { "pickn", run_test, pickn_regs, test_pickn },
+ { "quosqrt", run_quosqrt, quosqrt_regs, test_quosqrt },
+ { "sub-mulc-add-sub-mul", run_test,
+ sub_mulc_add_sub_mul_regs, test_sub_mulc_add_sub_mul },
+ { 0 }
+};
+
+int main(void)
+ { return run_test_suite(NROUT, NREG, sizeof(struct reg), tests, stdin); }