@@@ crypto-test
[secnet] / ec-field-test.c
diff --git a/ec-field-test.c b/ec-field-test.c
new file mode 100644 (file)
index 0000000..5c29704
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * 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&REGF_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, &regty_fe, 0 }
+#define REG_V { "v", RV, &regty_fe, 0 }
+#define REG_W { "w", RW, &regty_fe, 0 }
+#define REG_X { "x", RX, &regty_fe, 0 }
+#define REG_Y { "y", RY, &regty_fe, 0 }
+#define REG_A { "a", RA, &regty_int, 0 }
+#define REG_M { "m", RM, &regty_uint, 0 }
+#define REG_I { "i", RI, &regty_uint, 0 }
+#define REG_XX { "xx", RXX, &regty_fe, 0 }
+#define REG_YY { "yy", RYY, &regty_fe, 0 }
+#define REG_Z { "z", RZ, &regty_fe, 0 }
+#define REG_Z0 { "z0", RZ0, &regty_fe, 0 }
+#define REG_Z1 { "z1", RZ1, &regty_fe, 0 }
+#define REG_BIGY { "Y", RY, &regty_fe, 0 }
+#define REG_BIGZ { "Z", RZ, &regty_fe, 0 }
+#define REG_N { "n", RN, &regty_uint, 0 }
+#define REG_Vi(i) { "v[" # i "]", RV0 + i, &regty_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); }