| 1 | /* |
| 2 | * ec-field-test.c: test harness for elliptic-curve field arithmetic |
| 3 | * |
| 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.) |
| 7 | */ |
| 8 | /* |
| 9 | * This file is Free Software. It was originally written for secnet. |
| 10 | * |
| 11 | * Copyright 2017 Mark Wooding |
| 12 | * |
| 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 |
| 16 | * later version. |
| 17 | * |
| 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 |
| 21 | * version. |
| 22 | * |
| 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. |
| 27 | * |
| 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. |
| 31 | */ |
| 32 | |
| 33 | #include <stdio.h> |
| 34 | |
| 35 | #include "secnet.h" |
| 36 | |
| 37 | #include "f25519.h" |
| 38 | #include "fgoldi.h" |
| 39 | |
| 40 | #define f25519_FESZ 32u |
| 41 | #define fgoldi_FESZ 56u |
| 42 | |
| 43 | #define GLUE(x, y) GLUE_(x, y) |
| 44 | #define GLUE_(x, y) x##y |
| 45 | #define FIELDOP(op) GLUE(FIELD, _##op) |
| 46 | |
| 47 | #define REG_MEMBERS \ |
| 48 | struct { FIELD x; int ok; } fe; |
| 49 | #include "crypto-test.h" |
| 50 | |
| 51 | enum { |
| 52 | RZ, RZ0 = RZ, RZ1, RXX = RZ0, RYY = RZ1, NROUT, |
| 53 | RM = NROUT, RI = RM, RN = RM, |
| 54 | RU, RV, RW, RX, RY, RA, |
| 55 | RV0 = RU, RV31 = RV0 + 31, |
| 56 | NREG |
| 57 | }; |
| 58 | |
| 59 | static void init_fe(union regval *v) { v->fe.ok = 1; } |
| 60 | |
| 61 | static void parse_fe(union regval *v, char *p) |
| 62 | { |
| 63 | octet buf[FIELDOP(FESZ)]; |
| 64 | size_t n = strlen(p); |
| 65 | size_t sz = sizeof(buf); |
| 66 | |
| 67 | if (!*p) |
| 68 | v->fe.ok = 0; |
| 69 | else { |
| 70 | if (sz > n/2) sz = n/2; |
| 71 | parse_hex(buf, sz, p); memset(buf + sz, 0, sizeof(buf) - sz); |
| 72 | FIELDOP(load)(&v->fe.x, buf); |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | static void dump_fe(FILE *fp, const union regval *v) |
| 77 | { |
| 78 | octet buf[FIELDOP(FESZ)]; |
| 79 | |
| 80 | if (!v->fe.ok) |
| 81 | fprintf(fp, "nil\n"); |
| 82 | else { |
| 83 | FIELDOP(store)(buf, &v->fe.x); |
| 84 | dump_hex(fp, buf, sizeof(buf)); |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | static int eq_fe(const union regval *v0, const union regval *v1) |
| 89 | { |
| 90 | octet buf0[FIELDOP(FESZ)], buf1[FIELDOP(FESZ)]; |
| 91 | |
| 92 | if (!v0->fe.ok) |
| 93 | return (!v1->fe.ok); |
| 94 | else if (!v1->fe.ok) |
| 95 | return (0); |
| 96 | else { |
| 97 | FIELDOP(store)(buf0, &v0->fe.x); |
| 98 | FIELDOP(store)(buf1, &v1->fe.x); |
| 99 | return (memcmp(buf0, buf1, sizeof(buf0)) == 0); |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | static const struct regty regty_fe = { |
| 104 | init_fe, |
| 105 | parse_fe, |
| 106 | dump_fe, |
| 107 | eq_fe, |
| 108 | trivial_regty_release |
| 109 | }; |
| 110 | |
| 111 | #define BINOP(op) \ |
| 112 | static void test_##op(struct reg *out, \ |
| 113 | const struct reg *in, void *ctx) \ |
| 114 | { FIELDOP(op)(&out[RZ].v.fe.x, &in[RX].v.fe.x, &in[RY].v.fe.x); } |
| 115 | |
| 116 | #define UNOP(op) \ |
| 117 | static void test_##op(struct reg *out, \ |
| 118 | const struct reg *in, void *ctx) \ |
| 119 | { FIELDOP(op)(&out[RZ].v.fe.x, &in[RX].v.fe.x); } |
| 120 | |
| 121 | BINOP(add) |
| 122 | BINOP(sub) |
| 123 | BINOP(mul) |
| 124 | UNOP(neg) |
| 125 | UNOP(sqr) |
| 126 | UNOP(inv) |
| 127 | |
| 128 | static void test_condneg(struct reg *out, const struct reg *in, void *ctx) |
| 129 | { FIELDOP(condneg)(&out[RZ].v.fe.x, &in[RX].v.fe.x, in[RM].v.u); } |
| 130 | |
| 131 | static void test_mulconst(struct reg *out, const struct reg *in, void *ctx) |
| 132 | { FIELDOP(mulconst)(&out[RZ].v.fe.x, &in[RX].v.fe.x, in[RA].v.i); } |
| 133 | |
| 134 | static void test_condswap(struct reg *out, const struct reg *in, void *ctx) |
| 135 | { |
| 136 | FIELD *x = &out[RXX].v.fe.x, *y = &out[RYY].v.fe.x; |
| 137 | *x = in[RX].v.fe.x; *y = in[RY].v.fe.x; |
| 138 | FIELDOP(condswap)(x, y, in[RM].v.u); |
| 139 | } |
| 140 | |
| 141 | static void test_pick2(struct reg *out, const struct reg *in, void *ctx) |
| 142 | { |
| 143 | FIELDOP(pick2)(&out[RZ].v.fe.x, |
| 144 | &in[RX].v.fe.x, &in[RY].v.fe.x, in[RM].v.u); |
| 145 | } |
| 146 | |
| 147 | static void test_pickn(struct reg *out, const struct reg *in, void *ctx) |
| 148 | { |
| 149 | FIELD v[32]; |
| 150 | unsigned n; |
| 151 | |
| 152 | for (n = 0; in[RV0 + n].f®F_LIVE; n++) v[n] = in[RV0 + n].v.fe.x; |
| 153 | FIELDOP(pickn)(&out[RZ].v.fe.x, v, n, in[RI].v.u); |
| 154 | } |
| 155 | |
| 156 | static void test_quosqrt(struct reg *out, const struct reg *in, void *ctx) |
| 157 | { |
| 158 | if (FIELDOP(quosqrt)(&out[RZ0].v.fe.x, &in[RX].v.fe.x, &in[RY].v.fe.x)) |
| 159 | out[RZ0].v.fe.ok = 0; |
| 160 | } |
| 161 | |
| 162 | static void run_quosqrt(struct test_state *state, const struct test *test) |
| 163 | { |
| 164 | test->fn(state->out, state->in, 0); |
| 165 | |
| 166 | /* ..._quosqrt returns an arbitrary square root. The test vector |
| 167 | * contains both. We win if we match either. |
| 168 | * |
| 169 | * So: we always copy the expected Z1 into the computed-Z1 slot. If we |
| 170 | * got Z0 wrong, then the test will still fail. If we got Z0 right, then |
| 171 | * we'll pass. If our computed Z0 matches the expected Z1, then /also/ |
| 172 | * pretend we computed Z0 as expected, and then we'll pass. |
| 173 | */ |
| 174 | if (eq_fe(&state->in[RZ1].v, &state->out[RZ].v)) |
| 175 | state->out[RZ0].v = state->in[RZ0].v; |
| 176 | state->out[RZ1].v = state->in[RZ1].v; |
| 177 | check_test_output(state, test); |
| 178 | } |
| 179 | |
| 180 | static void test_sub_mulc_add_sub_mul(struct reg *out, |
| 181 | const struct reg *in, void *ctx) |
| 182 | { |
| 183 | FIELD t, u; |
| 184 | |
| 185 | FIELDOP(sub)(&t, &in[RU].v.fe.x, &in[RV].v.fe.x); |
| 186 | FIELDOP(mulconst)(&t, &t, in[RA].v.i); |
| 187 | FIELDOP(add)(&t, &t, &in[RW].v.fe.x); |
| 188 | FIELDOP(sub)(&u, &in[RX].v.fe.x, &in[RY].v.fe.x); |
| 189 | FIELDOP(mul)(&out[RZ].v.fe.x, &t, &u); |
| 190 | } |
| 191 | |
| 192 | #define REG_U { "u", RU, ®ty_fe, 0 } |
| 193 | #define REG_V { "v", RV, ®ty_fe, 0 } |
| 194 | #define REG_W { "w", RW, ®ty_fe, 0 } |
| 195 | #define REG_X { "x", RX, ®ty_fe, 0 } |
| 196 | #define REG_Y { "y", RY, ®ty_fe, 0 } |
| 197 | #define REG_A { "a", RA, ®ty_int, 0 } |
| 198 | #define REG_M { "m", RM, ®ty_uint, 0 } |
| 199 | #define REG_I { "i", RI, ®ty_uint, 0 } |
| 200 | #define REG_XX { "xx", RXX, ®ty_fe, 0 } |
| 201 | #define REG_YY { "yy", RYY, ®ty_fe, 0 } |
| 202 | #define REG_Z { "z", RZ, ®ty_fe, 0 } |
| 203 | #define REG_Z0 { "z0", RZ0, ®ty_fe, 0 } |
| 204 | #define REG_Z1 { "z1", RZ1, ®ty_fe, 0 } |
| 205 | #define REG_BIGY { "Y", RY, ®ty_fe, 0 } |
| 206 | #define REG_BIGZ { "Z", RZ, ®ty_fe, 0 } |
| 207 | #define REG_N { "n", RN, ®ty_uint, 0 } |
| 208 | #define REG_Vi(i) { "v[" # i "]", RV0 + i, ®ty_fe, REGF_OPT } |
| 209 | #define REG_VV \ |
| 210 | REG_Vi( 0), REG_Vi( 1), REG_Vi( 2), REG_Vi( 3), \ |
| 211 | REG_Vi( 4), REG_Vi( 5), REG_Vi( 6), REG_Vi( 7), \ |
| 212 | REG_Vi( 8), REG_Vi( 9), REG_Vi(10), REG_Vi(11), \ |
| 213 | REG_Vi(12), REG_Vi(13), REG_Vi(14), REG_Vi(15), \ |
| 214 | REG_Vi(16), REG_Vi(17), REG_Vi(18), REG_Vi(19), \ |
| 215 | REG_Vi(20), REG_Vi(21), REG_Vi(22), REG_Vi(23), \ |
| 216 | REG_Vi(24), REG_Vi(25), REG_Vi(26), REG_Vi(27), \ |
| 217 | REG_Vi(28), REG_Vi(29), REG_Vi(30), REG_Vi(31) |
| 218 | static const struct regdef |
| 219 | unop_regs[] = { REG_X, REG_Z, REGLIST_END }, |
| 220 | binop_regs[] = { REG_X, REG_Y, REG_Z, REGLIST_END }, |
| 221 | condneg_regs[] = { REG_X, REG_M, REG_Z, REGLIST_END }, |
| 222 | mulconst_regs[] = { REG_X, REG_A, REG_Z, REGLIST_END }, |
| 223 | pick2_regs[] = { REG_X, REG_Y, REG_M, REG_Z, REGLIST_END }, |
| 224 | pickn_regs[] = { REG_VV, REG_I, REG_Z, REGLIST_END }, |
| 225 | condswap_regs[] = { REG_X, REG_Y, REG_M, REG_XX, REG_YY, REGLIST_END }, |
| 226 | quosqrt_regs[] = { REG_X, REG_Y, REG_Z0, REG_Z1, REGLIST_END }, |
| 227 | sub_mulc_add_sub_mul_regs[] = |
| 228 | { REG_U, REG_V, REG_A, REG_W, REG_X, REG_Y, REG_Z, REGLIST_END }; |
| 229 | |
| 230 | static const struct test tests[] = { |
| 231 | { "add", run_test, binop_regs, test_add }, |
| 232 | { "sub", run_test, binop_regs, test_sub }, |
| 233 | { "neg", run_test, unop_regs, test_neg }, |
| 234 | { "condneg", run_test, condneg_regs, test_condneg }, |
| 235 | { "condswap", run_test, condswap_regs, test_condswap }, |
| 236 | { "mulconst", run_test, mulconst_regs, test_mulconst }, |
| 237 | { "mul", run_test, binop_regs, test_mul }, |
| 238 | { "sqr", run_test, unop_regs, test_sqr }, |
| 239 | { "inv", run_test, unop_regs, test_inv }, |
| 240 | { "pick2", run_test, pick2_regs, test_pick2 }, |
| 241 | { "pickn", run_test, pickn_regs, test_pickn }, |
| 242 | { "quosqrt", run_quosqrt, quosqrt_regs, test_quosqrt }, |
| 243 | { "sub-mulc-add-sub-mul", run_test, |
| 244 | sub_mulc_add_sub_mul_regs, test_sub_mulc_add_sub_mul }, |
| 245 | { 0 } |
| 246 | }; |
| 247 | |
| 248 | int main(void) |
| 249 | { return run_test_suite(NROUT, NREG, sizeof(struct reg), tests, stdin); } |