+/*
+ * 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); }