From eac0a45fdd670274c6e5c976dc3de5d50cb83262 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Sun, 29 Sep 2019 03:14:32 +0100 Subject: [PATCH] Build and test the new crypto toys * Add a collection of test programs. (The upstream test programs are too heavily entangled with infrastructure that I'd actually prefer to replace with something like this new stuff.) * Add some new features to the header files `unaligned.h' and `u64.h' that are needed by the new code. * Almost all of Catacomb's crypto code depends on a header called which does various low-level bit operations. Provide a fake version, `fake-mLib-bits.h', which implements the necessary things in terms of C99 and what we already have lying around here in `unaligned.h' and `u64.h'. With that, we have enough to build and run the tests and watch them all pass. Signed-off-by: Mark Wooding --- .gitignore | 16 ++ Makefile.in | 36 ++++- crypto-test.c | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ crypto-test.h | 140 +++++++++++++++++ ec-field-test.c | 249 +++++++++++++++++++++++++++++ ed25519-test.c | 147 +++++++++++++++++ ed448-test.c | 121 ++++++++++++++ fake-mLib-bits.h | 83 ++++++++++ keccak1600-test.c | 72 +++++++++ sha3-test.c | 184 ++++++++++++++++++++++ u64.h | 17 ++ unaligned.h | 7 + xdh-test.c | 105 +++++++++++++ 13 files changed, 1638 insertions(+), 2 deletions(-) create mode 100644 crypto-test.c create mode 100644 crypto-test.h create mode 100644 ec-field-test.c create mode 100644 ed25519-test.c create mode 100644 ed448-test.c create mode 100644 fake-mLib-bits.h create mode 100644 keccak1600-test.c create mode 100644 sha3-test.c create mode 100644 xdh-test.c diff --git a/.gitignore b/.gitignore index bf9a085..381246c 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,22 @@ conffile.yy.[ch] msgcode-test msgcode-test.confirm +f25519-test +f25519-test.confirm +f448-test +f448-test.confirm +ed25519-test +ed25519-test.confirm +ed448-test +ed448-test.confirm +keccak1600-test +keccak1600-test.confirm +sha3-test +sha3-test.confirm +x25519-test +x25519-test.confirm +x448-test +x448-test.confirm autom4te.cache diff --git a/Makefile.in b/Makefile.in index 6bc02ef..40407b9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -67,8 +67,11 @@ OBJECTS:=secnet.o util.o conffile.yy.o conffile.tab.o conffile.o modules.o \ hackypar.o # version.o is handled specially below and in the link rule for secnet. +CRYPTO_TESTS = keccak1600 sha3 f25519 x25519 ed25519 fgoldi x448 ed448 + TEST_OBJECTS:=eax-aes-test.o eax-serpent-test.o eax-serpentbe-test.o \ - eax-test.o aes.o + eax-test.o aes.o \ + $(addsuffix -test.o,$(CRYPTO_TESTS)) ifeq (version.o,$(MAKECMDGOALS)) OBJECTS:=version.o @@ -145,7 +148,8 @@ endif check: eax-aes-test.confirm eax-serpent-test.confirm \ eax-serpentbe-test.confirm check-ipaddrset \ - msgcode-test.confirm + msgcode-test.confirm \ + $(foreach c,$(CRYPTO_TESTS),$c-test.confirm) version.c: Makefile echo "#include \"secnet.h\"" >$@.new @@ -172,6 +176,32 @@ msgcode-test.confirm: msgcode-test ./msgcode-test touch $@ +$(foreach c,$(CRYPTO_TESTS),$c-test): %-test: %-test.o crypto-test.o + $(CC) $(LDFLAGS) $(ALL_CFLAGS) -o $@ $^ + +$(foreach c,$(CRYPTO_TESTS),$c-test.confirm): \ + %-test.confirm: %-test %-tests.in + ./$*-test <$(srcdir)/$*-tests.in + touch $@ + +keccak1600-test: keccak1600.o +sha3-test: sha3.o keccak1600.o +f25519-test: f25519.o +x25519-test: x25519.o f25519.o +ed25519-test: sha512.o f25519.o scaf.o ed25519.o +fgoldi-test: fgoldi.o +x448-test: x448.o fgoldi.o +ed448-test: keccak1600.o sha3.o fgoldi.o scaf.o ed448.o + +f25519-test.o: ec-field-test.c + $(CC) $(CPPFLAGS) $(ALL_CFLAGS) -c -DFIELD=f25519 $< -o $@ +fgoldi-test.o: ec-field-test.c + $(CC) $(CPPFLAGS) $(ALL_CFLAGS) -c -DFIELD=fgoldi $< -o $@ +x25519-test.o: xdh-test.c + $(CC) $(CPPFLAGS) $(ALL_CFLAGS) -c -DXDH=x25519 -DFIELD=f25519 $< -o $@ +x448-test.o: xdh-test.c + $(CC) $(CPPFLAGS) $(ALL_CFLAGS) -c -DXDH=x448 -DFIELD=fgoldi $< -o $@ + check-ipaddrset: ipaddrset-test.py ipaddrset.py ipaddrset-test.expected $(srcdir)/ipaddrset-test.py >ipaddrset-test.new diff -u $(srcdir)/ipaddrset-test.expected ipaddrset-test.new @@ -207,6 +237,8 @@ clean: $(RM) -f *.o *.yy.[ch] *.tab.[ch] $(TARGETS) core version.c $(RM) -f *.d *.pyc *~ eax-*-test.confirm eax-*-test $(RM) -f msgcode-test.confirm msgcode-test + $(RM) -f $(addsuffix -test, $(CRYPTO_TESTS)) + $(RM) -f $(addsuffix -test.confirm, $(CRYPTO_TESTS)) realclean: clean $(RM) -f *~ Makefile config.h *.d \ diff --git a/crypto-test.c b/crypto-test.c new file mode 100644 index 0000000..4c7c2e3 --- /dev/null +++ b/crypto-test.c @@ -0,0 +1,463 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#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. sha512.c wants to drag in the world. */ +void *safe_malloc(size_t size, const char *message) { return xmalloc(size); } +list_t *new_closure(closure_t *cl) { abort(); } +void dict_add(dict_t *dict, cstring_t key, list_t *val) { abort(); } + +/* 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 +}; + +/* Text strings. Not really intended as an output type. */ + +void allocate_string(union regval *v, size_t sz) + { v->str.p = xmalloc(sz + 1); v->str.sz = sz; } + +static void init_string(union regval *v) { v->str.p = 0; v->str.sz = 0; } + +static void parse_string(union regval *v, char *p) +{ + size_t n = strlen(p); + + allocate_string(v, n); + memcpy(v->str.p, p, n + 1); +} + +static void dump_string(FILE *fp, const union regval *v) +{ + if (v->str.p) fprintf(fp, "`%s'\n", v->str.p); + else fputs("nil\n", fp); +} + +static int eq_string(const union regval *v0, const union regval *v1) +{ + size_t n0 = v0->str.sz, n1 = v1->str.sz, n = n0 < n1 ? n0 : n1; + return !strncmp(v0->str.p, v1->str.p, n); +} + +static void release_string(union regval *v) { free(v->str.p); } + +const struct regty regty_string = { + init_string, + parse_string, + dump_string, + eq_string, + release_string +}; + +/*----- 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 (!(in->f®F_LIVE)) continue; + 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; +} diff --git a/crypto-test.h b/crypto-test.h new file mode 100644 index 0000000..807ae07 --- /dev/null +++ b/crypto-test.h @@ -0,0 +1,140 @@ +/* + * 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 { unsigned char *p; size_t sz; } bytes; /* buffer of bytes */ + struct { char *p; size_t sz; } str; /* text string */ +#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); +extern void allocate_string(union regval *v, size_t sz); + +/* Built-in register types. */ +extern const struct regty + regty_int, + regty_uint, + regty_bytes, + regty_string; + +/* 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 diff --git a/ec-field-test.c b/ec-field-test.c new file mode 100644 index 0000000..51b3e64 --- /dev/null +++ b/ec-field-test.c @@ -0,0 +1,249 @@ +/* + * 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 + +#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 \ + struct { FIELD x; int ok; } fe; +#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 init_fe(union regval *v) { v->fe.ok = 1; } + +static void parse_fe(union regval *v, char *p) +{ + octet buf[FIELDOP(FESZ)]; + size_t n = strlen(p); + size_t sz = sizeof(buf); + + if (!*p) + v->fe.ok = 0; + else { + if (sz > n/2) sz = n/2; + parse_hex(buf, sz, p); memset(buf + sz, 0, sizeof(buf) - sz); + FIELDOP(load)(&v->fe.x, buf); + } +} + +static void dump_fe(FILE *fp, const union regval *v) +{ + octet buf[FIELDOP(FESZ)]; + + if (!v->fe.ok) + fprintf(fp, "nil\n"); + else { + FIELDOP(store)(buf, &v->fe.x); + dump_hex(fp, buf, sizeof(buf)); + } +} + +static int eq_fe(const union regval *v0, const union regval *v1) +{ + octet buf0[FIELDOP(FESZ)], buf1[FIELDOP(FESZ)]; + + if (!v0->fe.ok) + return (!v1->fe.ok); + else if (!v1->fe.ok) + return (0); + else { + FIELDOP(store)(buf0, &v0->fe.x); + FIELDOP(store)(buf1, &v1->fe.x); + return (memcmp(buf0, buf1, sizeof(buf0)) == 0); + } +} + +static const struct regty regty_fe = { + init_fe, + 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) \ + { FIELDOP(op)(&out[RZ].v.fe.x, &in[RX].v.fe.x, &in[RY].v.fe.x); } + +#define UNOP(op) \ + static void test_##op(struct reg *out, \ + const struct reg *in, void *ctx) \ + { FIELDOP(op)(&out[RZ].v.fe.x, &in[RX].v.fe.x); } + +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) + { FIELDOP(condneg)(&out[RZ].v.fe.x, &in[RX].v.fe.x, in[RM].v.u); } + +static void test_mulconst(struct reg *out, const struct reg *in, void *ctx) + { FIELDOP(mulconst)(&out[RZ].v.fe.x, &in[RX].v.fe.x, in[RA].v.i); } + +static void test_condswap(struct reg *out, const struct reg *in, void *ctx) +{ + FIELD *x = &out[RXX].v.fe.x, *y = &out[RYY].v.fe.x; + *x = in[RX].v.fe.x; *y = in[RY].v.fe.x; + FIELDOP(condswap)(x, y, in[RM].v.u); +} + +static void test_pick2(struct reg *out, const struct reg *in, void *ctx) +{ + FIELDOP(pick2)(&out[RZ].v.fe.x, + &in[RX].v.fe.x, &in[RY].v.fe.x, in[RM].v.u); +} + +static void test_pickn(struct reg *out, const struct reg *in, void *ctx) +{ + FIELD v[32]; + unsigned n; + + for (n = 0; in[RV0 + n].f®F_LIVE; n++) v[n] = in[RV0 + n].v.fe.x; + FIELDOP(pickn)(&out[RZ].v.fe.x, v, n, in[RI].v.u); +} + +static void test_quosqrt(struct reg *out, const struct reg *in, void *ctx) +{ + if (FIELDOP(quosqrt)(&out[RZ0].v.fe.x, &in[RX].v.fe.x, &in[RY].v.fe.x)) + out[RZ0].v.fe.ok = 0; +} + +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. + * + * So: we always copy the expected Z1 into the computed-Z1 slot. If we + * got Z0 wrong, then the test will still fail. If we got Z0 right, then + * we'll pass. If our computed Z0 matches the expected Z1, then /also/ + * pretend we computed Z0 as expected, and then we'll pass. + */ + 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 t, u; + + FIELDOP(sub)(&t, &in[RU].v.fe.x, &in[RV].v.fe.x); + FIELDOP(mulconst)(&t, &t, in[RA].v.i); + FIELDOP(add)(&t, &t, &in[RW].v.fe.x); + FIELDOP(sub)(&u, &in[RX].v.fe.x, &in[RY].v.fe.x); + FIELDOP(mul)(&out[RZ].v.fe.x, &t, &u); +} + +#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); } diff --git a/ed25519-test.c b/ed25519-test.c new file mode 100644 index 0000000..ce5b9ef --- /dev/null +++ b/ed25519-test.c @@ -0,0 +1,147 @@ +/* + * ed25519-test.c: test harness for elliptic curve signatures + * + * (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 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 + +#include "secnet.h" + +#include "sha512.h" +#include "ed25519.h" + +#define GLUE(x, y) GLUE_(x, y) +#define GLUE_(x, y) x##y +#define XDHOP(op) GLUE(XDH, _##op) + +#include "crypto-test.h" + +enum { + RSIGOUT, RAOUT = RSIGOUT, RRC = RSIGOUT, NROUT, + RA = NROUT, RPH, RCTX, RM, RSIGIN, NREG +}; + +static void test_pubkey(struct reg *out, const struct reg *in, void *ctx) +{ + allocate_bytes(&out[RAOUT].v, ED25519_PUBSZ); + ed25519_pubkey(out[RAOUT].v.bytes.p, + in[RA].v.bytes.p, in[RA].v.bytes.sz); +} + +static void test_sign(struct reg *out, const struct reg *in, void *ctx) +{ + octet K[ED25519_PUBSZ]; + + allocate_bytes(&out[RSIGOUT].v, ED25519_SIGSZ); + ed25519_pubkey(K, in[RA].v.bytes.p, in[RA].v.bytes.sz); + ed25519_sign(out[RSIGOUT].v.bytes.p, + in[RA].v.bytes.p, in[RA].v.bytes.sz, K, + in[RM].v.bytes.p, in[RM].v.bytes.sz); +} + +static void test_sign_ctx(struct reg *out, const struct reg *in, void *ctx) +{ + octet K[ED25519_PUBSZ]; + const octet *m = in[RM].v.bytes.p; size_t msz = in[RM].v.bytes.sz; + octet h[SHA512_DIGEST_SIZE]; + struct sha512_ctx hctx; + + if (in[RPH].v.i) { + sha512_init_ctx(&hctx); + sha512_process_bytes(m, msz, &hctx); + sha512_finish_ctx(&hctx, h); + m = h; msz = sizeof(h); + } + + allocate_bytes(&out[RSIGOUT].v, ED25519_SIGSZ); + ed25519_pubkey(K, in[RA].v.bytes.p, in[RA].v.bytes.sz); + ed25519ctx_sign(out[RSIGOUT].v.bytes.p, + in[RA].v.bytes.p, in[RA].v.bytes.sz, K, + in[RPH].v.i, + in[RCTX].v.bytes.p, in[RCTX].v.bytes.sz, + m, msz); +} + +static void test_verify(struct reg *out, const struct reg *in, void *ctx) +{ + out[RRC].v.i = ed25519_verify(in[RA].v.bytes.p, + in[RM].v.bytes.p, in[RM].v.bytes.sz, + in[RSIGIN].v.bytes.p); +} + +static void test_verify_ctx(struct reg *out, const struct reg *in, void *ctx) +{ + const octet *m = in[RM].v.bytes.p; size_t msz = in[RM].v.bytes.sz; + octet h[SHA512_DIGEST_SIZE]; + struct sha512_ctx hctx; + + if (in[RPH].v.i) { + sha512_init_ctx(&hctx); + sha512_process_bytes(m, msz, &hctx); + sha512_finish_ctx(&hctx, h); + m = h; msz = sizeof(h); + } + + out[RRC].v.i = ed25519ctx_verify(in[RA].v.bytes.p, + in[RPH].v.i, + in[RCTX].v.bytes.p, in[RCTX].v.bytes.sz, + m, msz, in[RSIGIN].v.bytes.p); +} + +#define REG_A { "a", RA, ®ty_bytes, 0 } +#define REG_BIGA { "A", RA, ®ty_bytes, 0 } +#define REG_PH { "ph", RPH, ®ty_int, 0 } +#define REG_CTX { "ctx", RCTX, ®ty_bytes, 0 } +#define REG_M { "m", RM, ®ty_bytes, 0 } +#define REG_SIGIN { "sig", RSIGIN, ®ty_bytes, 0 } + +#define REG_SIGOUT { "sig", RSIGOUT, ®ty_bytes, 0 } +#define REG_AOUT { "A", RAOUT, ®ty_bytes, 0 } +#define REG_RC { "rc", RRC, ®ty_int, 0 } +static const struct regdef + pubkey_regs[] = { REG_A, REG_AOUT, REGLIST_END }, + sign_regs[] = { REG_A, REG_M, REG_SIGOUT, REGLIST_END }, + sign_ctx_regs[] = { REG_A, REG_PH, REG_CTX, + REG_M, REG_SIGOUT, REGLIST_END }, + verify_regs[] = { REG_BIGA, REG_M, REG_SIGIN, REG_RC, REGLIST_END }, + verify_ctx_regs[] = { REG_BIGA, REG_PH, REG_CTX, + REG_M, REG_SIGIN, REG_RC, REGLIST_END }; + +static const struct test tests[] = { + { "pubkey", run_test, pubkey_regs, test_pubkey }, + { "sign", run_test, sign_regs, test_sign }, + { "sign-ctx", run_test, sign_ctx_regs, test_sign_ctx }, + { "verify", run_test, verify_regs, test_verify }, + { "verify-ctx", run_test, verify_ctx_regs, test_verify_ctx }, + { 0 } +}; + +int main(void) + { return run_test_suite(NROUT, NREG, sizeof(struct reg), tests, stdin); } diff --git a/ed448-test.c b/ed448-test.c new file mode 100644 index 0000000..b240ace --- /dev/null +++ b/ed448-test.c @@ -0,0 +1,121 @@ +/* + * ed448-test.c: test harness for elliptic curve signatures + * + * (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 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 + +#include "secnet.h" + +#include "sha3.h" +#include "ed448.h" + +#include "crypto-test.h" + +enum { + RSIGOUT, RAOUT = RSIGOUT, RRC = RSIGOUT, NROUT, + RA = NROUT, RPH, RCTX, RM, RSIGIN, NREG +}; + +static void test_pubkey(struct reg *out, const struct reg *in, void *ctx) +{ + allocate_bytes(&out[RAOUT].v, ED448_PUBSZ); + ed448_pubkey(out[RAOUT].v.bytes.p, + in[RA].v.bytes.p, in[RA].v.bytes.sz); +} + +static void test_sign(struct reg *out, const struct reg *in, void *ctx) +{ + octet K[ED448_PUBSZ]; + const octet *m = in[RM].v.bytes.p; size_t msz = in[RM].v.bytes.sz; + octet h[64]; + shake_ctx hctx; + + if (in[RPH].v.i) { + shake256_init(&hctx); + shake_hash(&hctx, m, msz); + shake_done(&hctx, h, sizeof(h)); + m = h; msz = sizeof(h); + } + + allocate_bytes(&out[RSIGOUT].v, ED448_SIGSZ); + ed448_pubkey(K, in[RA].v.bytes.p, in[RA].v.bytes.sz); + ed448_sign(out[RSIGOUT].v.bytes.p, + in[RA].v.bytes.p, in[RA].v.bytes.sz, K, + in[RPH].v.i, + in[RCTX].v.bytes.p, in[RCTX].v.bytes.sz, + m, msz); +} + +static void test_verify(struct reg *out, const struct reg *in, void *ctx) +{ + const octet *m = in[RM].v.bytes.p; size_t msz = in[RM].v.bytes.sz; + octet h[64]; + shake_ctx hctx; + + if (in[RPH].v.i) { + shake256_init(&hctx); + shake_hash(&hctx, m, msz); + shake_done(&hctx, h, sizeof(h)); + m = h; msz = sizeof(h); + } + + out[RRC].v.i = ed448_verify(in[RA].v.bytes.p, + in[RPH].v.i, + in[RCTX].v.bytes.p, in[RCTX].v.bytes.sz, + m, msz, in[RSIGIN].v.bytes.p); +} + +#define REG_A { "a", RA, ®ty_bytes, 0 } +#define REG_BIGA { "A", RA, ®ty_bytes, 0 } +#define REG_PH { "ph", RPH, ®ty_int, 0 } +#define REG_CTX { "ctx", RCTX, ®ty_bytes, 0 } +#define REG_M { "m", RM, ®ty_bytes, 0 } +#define REG_SIGIN { "sig", RSIGIN, ®ty_bytes, 0 } + +#define REG_SIGOUT { "sig", RSIGOUT, ®ty_bytes, 0 } +#define REG_AOUT { "A", RAOUT, ®ty_bytes, 0 } +#define REG_RC { "rc", RRC, ®ty_int, 0 } +static const struct regdef + pubkey_regs[] = { REG_A, REG_AOUT, REGLIST_END }, + sign_regs[] = { REG_A, REG_PH, REG_CTX, + REG_M, REG_SIGOUT, REGLIST_END }, + verify_regs[] = { REG_BIGA, REG_PH, REG_CTX, + REG_M, REG_SIGIN, REG_RC, REGLIST_END }; + +static const struct test tests[] = { + { "pubkey", run_test, pubkey_regs, test_pubkey }, + { "sign", run_test, sign_regs, test_sign }, + { "verify", run_test, verify_regs, test_verify }, + { 0 } +}; + +int main(void) + { return run_test_suite(NROUT, NREG, sizeof(struct reg), tests, stdin); } diff --git a/fake-mLib-bits.h b/fake-mLib-bits.h new file mode 100644 index 0000000..e2e83ca --- /dev/null +++ b/fake-mLib-bits.h @@ -0,0 +1,83 @@ +/* + * This file is part of secnet. + * See README for full list of copyright holders. + * + * secnet is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version d of the License, or + * (at your option) any later version. + * + * secnet 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 + * version 3 along with secnet; if not, see + * https://www.gnu.org/licenses/gpl.html. + */ + +#ifndef fake_mLib_bits_h +#define fake_mLib_bits_h + +/* The header defines a large number of types and macros for + * various kinds of bithacking. Notably, it has machinery for autodetecting + * suitable types holding (at least) various numbers of bits -- a service + * which C99 provides through other means. + * + * This file provides a small portion of the interface, just + * enough to make the pieces of code lifted from Catacomb feel at home. + */ + +#include +#include "u64.h" +#include "unaligned.h" + +typedef uint8_t octet; +typedef uint32_t uint32; +typedef uint64_t uint64; + +typedef int32_t int32; +typedef int64_t int64; + +#define LOAD32_L(p) (get_uint32_le(p)) +#define LOAD32_B(p) (get_uint32(p)) +#define STORE32_L(p, x) put_uint32_le((p), (x)) +#define STORE32_B(p, x) put_uint32((p), (x)) +#define ROL32(x, n) ((n) ? ((x) << (n) | (x) >> (32 - (n))) : (x)) + +#define MASK32 0xffffffffu +#define U32(x) ((uint32)(x) & MASK32) + +typedef u64 kludge64; +#define X64(hi, lo) u64init(0x##hi, 0x##lo) +#define HI64(x) u64gethi(x) +#define LO64(x) u64getlo(x) +#define LOAD64_L_(x, p) do { \ + const uint8_t *p_ = (const uint8_t *)(p); \ + uint32_t lo_ = LOAD32_L(p_ + 0), hi_ = LOAD32_L(p_ + 4); \ + (x) = u64hilo(hi_, lo_); \ +} while (0) +#define LOAD64_B_(x, p) do { \ + const uint8_t *p_ = (const uint8_t *)(p); \ + uint32_t hi_ = LOAD32_B(p_ + 0), lo_ = LOAD32_B(p_ + 4); \ + (x) = u64hilo(hi_, lo_); \ +} while (0) +#define STORE64_L_(p, x) do { \ + uint8_t *p_ = (uint8_t *)(p); \ + uint32_t lo_ = LO64(x), hi_ = HI64(x); \ + STORE32_L(p_ + 0, lo_); STORE32_L(p_ + 4, hi_); \ +} while (0) +#define STORE64_B_(p, x) do { \ + uint8_t *p_ = (uint8_t *)(p); \ + uint32_t lo_ = LO64(x), hi_ = HI64(x); \ + STORE32_B(p_ + 0, hi_); STORE32_B(p_ + 4, lo_); \ +} while (0) +#define SET64(z, hi, lo) ((z) = u64hilo((hi), (lo))) +#define AND64(z, x, y) ((z) = u64and((x), (y))) +#define OR64(z, x, y) ((z) = u64or((x), (y))) +#define XOR64(z, x, y) ((z) = u64xor((x), (y))) +#define CPL64(z, x) ((z) = u64not((x))) +#define ROL64_(z, x, n) ((n) ? (z) = u64rol((x), (n)) : (x)) + +#endif /* fake_mLib_bits_h */ diff --git a/keccak1600-test.c b/keccak1600-test.c new file mode 100644 index 0000000..e0102ac --- /dev/null +++ b/keccak1600-test.c @@ -0,0 +1,72 @@ +/* + * keccak1600-test.c: test harness for Keccak primitive + * + * (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 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 + +#include "secnet.h" + +#include "keccak1600.h" +#include "crypto-test.h" + +enum { + RZ, NROUT, + RX = NROUT, RN, NREG +}; + +static void test_p(struct reg *out, const struct reg *in, void *ctx) +{ + keccak1600_state u; + kludge64 t[25]; + unsigned i; + + allocate_bytes(&out[RZ].v, 200); + keccak1600_init(&u); + for (i = 0; i < 25; i++) LOAD64_L_(t[i], in[RX].v.bytes.p + 8*i); + keccak1600_mix(&u, t, 25); + keccak1600_p(&u, &u, in[RN].v.u); + keccak1600_extract(&u, t, 25); + for (i = 0; i < 25; i++) STORE64_L_(out[RZ].v.bytes.p + 8*i, t[i]); +} + +#define REG_X { "x", RX, ®ty_bytes, 0 } +#define REG_N { "n", RN, ®ty_uint, 0 } +#define REG_Z { "z", RZ, ®ty_bytes, 0 } +static const struct regdef + p_regs[] = { REG_X, REG_N, REG_Z, REGLIST_END }; + +static const struct test tests[] = { + { "p", run_test, p_regs, test_p }, + { 0 } +}; + +int main(void) + { return run_test_suite(NROUT, NREG, sizeof(struct reg), tests, stdin); } diff --git a/sha3-test.c b/sha3-test.c new file mode 100644 index 0000000..81d7610 --- /dev/null +++ b/sha3-test.c @@ -0,0 +1,184 @@ +/* + * sha3-test.c: test harness for SHA3 and related functions + * + * (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 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 + +#include "secnet.h" + +#include "keccak1600.h" +#include "sha3.h" +#include "crypto-test.h" + +enum { + RH, NROUT, + RM = NROUT, RN, RFUNC, RPERSO, NREG +}; + +struct test_context { + size_t step; + size_t outsz; +}; + +static void run_sha3_kat(struct test_state *state, const struct test *test) +{ + static const unsigned steps[] = { 1, 7, 192, -1 }; + unsigned i = 0; + struct test_context ctx; + + ctx.outsz = state->in[RH].v.bytes.sz; + do { + ctx.step = steps[i]; + test->fn(state->out, state->in, &ctx); + check_test_output(state, test); + } while (steps[i++] != (unsigned)-1); +} + +#define SHA3_VARIANTS(_) _(224) _(256) _(384) _(512) + +#define SHA3_TESTFUNCS(w) \ + \ +static void test_sha3_##w##_kat(struct reg *out, \ + const struct reg *in, void *vctx) \ +{ \ + struct test_context *ctx = vctx; \ + sha3_ctx hctx; \ + const octet *p = in[RM].v.bytes.p; \ + size_t sz = in[RM].v.bytes.sz, n; \ + \ + allocate_bytes(&out[RH].v, SHA3_##w##_HASHSZ); \ + sha3_##w##_init(&hctx); \ + while (sz) { \ + n = ctx->step; if (n > sz) n = sz; \ + sha3_hash(&hctx, p, n); p += n; sz -= n; \ + } \ + sha3_done(&hctx, out[RH].v.bytes.p); \ +} \ + \ +static void test_sha3_##w##_mct(struct reg *out, \ + const struct reg *in, void *vctx) \ +{ \ + sha3_ctx hctx; \ + octet *p; \ + unsigned i, n = in[RN].v.u; \ + \ + allocate_bytes(&out[RH].v, SHA3_##w##_HASHSZ); \ + p = out[RH].v.bytes.p; \ + memcpy(p, in[RM].v.bytes.p, SHA3_##w##_HASHSZ); \ + for (i = 0; i < n; i++) { \ + sha3_##w##_init(&hctx); \ + sha3_hash(&hctx, p, SHA3_##w##_HASHSZ); \ + sha3_done(&hctx, p); \ + } \ +} + +SHA3_VARIANTS(SHA3_TESTFUNCS) + +#define SHAKE_VARIANTS(_) _(128) _(256) + +#define SHAKE_TESTFUNCS(w) \ + \ +static void test_shake##w##_kat(struct reg *out, \ + const struct reg *in, void *vctx) \ +{ \ + struct test_context *ctx = vctx; \ + shake_ctx hctx; \ + const octet *p = in[RM].v.bytes.p; octet *q; \ + size_t sz = in[RM].v.bytes.sz, n; \ + \ + allocate_bytes(&out[RH].v, ctx->outsz); \ + shake##w##_init(&hctx); \ + while (sz) { \ + n = ctx->step; if (n > sz) n = sz; \ + shake_hash(&hctx, p, n); p += n; sz -= n; \ + } \ + shake_xof(&hctx); \ + q = out[RH].v.bytes.p; sz = ctx->outsz; \ + while (sz) { \ + n = ctx->step; if (n > sz) n = sz; \ + shake_get(&hctx, q, n); q += n; sz -= n; \ + } \ +} \ + \ +static void test_cshake##w##_kat(struct reg *out, \ + const struct reg *in, void *vctx) \ +{ \ + struct test_context *ctx = vctx; \ + shake_ctx hctx; \ + const octet *p = in[RM].v.bytes.p; octet *q; \ + size_t sz = in[RM].v.bytes.sz, n; \ + \ + allocate_bytes(&out[RH].v, ctx->outsz); \ + cshake##w##_init(&hctx, \ + in[RFUNC].v.str.p, in[RFUNC].v.str.sz, \ + in[RPERSO].v.str.p, in[RPERSO].v.str.sz); \ + while (sz) { \ + n = ctx->step; if (n > sz) n = sz; \ + shake_hash(&hctx, p, n); p += n; sz -= n; \ + } \ + shake_xof(&hctx); \ + q = out[RH].v.bytes.p; sz = ctx->outsz; \ + while (sz) { \ + n = ctx->step; if (n > sz) n = sz; \ + shake_get(&hctx, q, n); q += n; sz -= n; \ + } \ +} + +SHAKE_VARIANTS(SHAKE_TESTFUNCS) + +#define REG_M { "m", RM, ®ty_bytes, 0 } +#define REG_N { "n", RN, ®ty_uint, 0 } +#define REG_FUNC { "func", RFUNC, ®ty_string, 0 } +#define REG_PERSO { "perso", RPERSO, ®ty_string, 0 } +#define REG_H { "h", RH, ®ty_bytes, 0 } +static const struct regdef + hash_kat_regs[] = { REG_M, REG_H, REGLIST_END }, + hash_mct_regs[] = { REG_M, REG_N, REG_H, REGLIST_END }, + cshake_regs[] = { REG_M, REG_FUNC, REG_PERSO, REG_H, REGLIST_END }; + +#define SHA3_TESTDEFS(w) \ + { "sha3-" #w "-hex", run_sha3_kat, \ + hash_kat_regs, test_sha3_##w##_kat }, \ + { "sha3-" #w "-mct", run_test, \ + hash_mct_regs, test_sha3_##w##_mct }, +#define SHAKE_TESTDEFS(w) \ + { "shake" #w, run_sha3_kat, \ + hash_kat_regs, test_shake##w##_kat }, \ + { "cshake" #w, run_sha3_kat, \ + cshake_regs, test_cshake##w##_kat }, +static const struct test tests[] = { + SHA3_VARIANTS(SHA3_TESTDEFS) + SHAKE_VARIANTS(SHAKE_TESTDEFS) + { 0 } +}; + +int main(void) + { return run_test_suite(NROUT, NREG, sizeof(struct reg), tests, stdin); } diff --git a/u64.h b/u64.h index 0d35c55..25f4d0a 100644 --- a/u64.h +++ b/u64.h @@ -31,10 +31,13 @@ typedef uint64_t u64; # define u64hilo(hi, lo) ((u64) (((u64) (hi) << 32) + (lo))) # define u64init(hi, lo) u64hilo (hi, lo) # define u64lo(x) ((u64) (x)) +# define u64getlo(x) ((x)&0xffffffffu) +# define u64gethi(x) (((u64) (x) >> 32) & 0xffffffffu) # define u64lt(x, y) ((x) < (y)) # define u64and(x, y) ((x) & (y)) # define u64or(x, y) ((x) | (y)) # define u64xor(x, y) ((x) ^ (y)) +# define u64not(x) (~(x)) # define u64plus(x, y) ((x) + (y)) # define u64shl(x, n) ((x) << (n)) # define u64shr(x, n) ((x) >> (n)) @@ -73,6 +76,10 @@ u64lo (uint32_t lo) return r; } +/* Return the high and low halves of X. */ +# define u64getlo(x) ((x).lo) +# define u64gethi(x) ((x).hi) + /* Return X < Y. */ static inline int u64lt (u64 x, u64 y) @@ -110,6 +117,16 @@ u64xor (u64 x, u64 y) return r; } +/* Return ~X. */ +static inline u64 +u64not (u64 x) +{ + u64 r; + r.hi = ~x.hi; + r.lo = ~x.lo; + return r; +} + /* Return X + Y. */ static inline u64 u64plus (u64 x, u64 y) diff --git a/unaligned.h b/unaligned.h index 2c48d0e..6f4f163 100644 --- a/unaligned.h +++ b/unaligned.h @@ -31,6 +31,9 @@ #define put_uint32(a,v) do { (a)[0]=(v)>>24; (a)[1]=((v)&0xff0000)>>16; \ (a)[2]=((v)&0xff00)>>8; (a)[3]=(v)&0xff; } while(0) +#define put_uint32_le(a, v) do { (a)[0]=(v)&0xff; (a)[1]=((v)&0xff00)>>8; \ +(a)[2]=((v)&0xff0000)>>16; (a)[3]=(v)>>24; } while(0) + #define put_uint16(a,v) do {(a)[0]=((v)&0xff00)>>8; (a)[1]=(v)&0xff;} while(0) #define put_uint8(a,v) do {(a)[0]=((v)&0xff);} while(0) @@ -39,6 +42,10 @@ (((uint32_t)(a)[0]<<24) | ((uint32_t)(a)[1]<<16) | \ ((uint32_t)(a)[2]<<8) | (uint32_t)(a)[3]) +#define get_uint32_le(a) \ + (((uint32_t)(a)[0]) | ((uint32_t)(a)[1]<<8) | \ + ((uint32_t)(a)[2]<<16) | (uint32_t)(a)[3]<<24) + #define get_uint16(a) (((uint16_t)(a)[0]<<8)|(uint16_t)(a)[1]) #define get_uint8(a) (((uint8_t)(a)[0])) diff --git a/xdh-test.c b/xdh-test.c new file mode 100644 index 0000000..08efabe --- /dev/null +++ b/xdh-test.c @@ -0,0 +1,105 @@ +/* + * xdh-test.c: test harness for elliptic-curve Diffie--Hellman + * + * (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 + +#include "secnet.h" + +#include "x25519.h" +#include "x448.h" + +#define x25519_KEYSZ X25519_KEYSZ +#define x448_KEYSZ X448_KEYSZ + +#define GLUE(x, y) GLUE_(x, y) +#define GLUE_(x, y) x##y +#define XDHOP(op) GLUE(XDH, _##op) + +#define REG_MEMBERS \ + uint8_t k[XDHOP(KEYSZ)]; +#include "crypto-test.h" + +enum { + RZ, NROUT, + RN = NROUT, RX, RY, NREG +}; + +static void parse_key(union regval *v, char *p) + { parse_hex(v->k, sizeof(v->k), p); } + +static void dump_key(FILE *fp, const union regval *v) + { dump_hex(fp, v->k, sizeof(v->k)); } + +static int eq_key(const union regval *v0, const union regval *v1) + { return (memcmp(v0->k, v1->k, sizeof(v0->k)) == 0); } + +static const struct regty regty_key = { + trivial_regty_init, + parse_key, + dump_key, + eq_key, + trivial_regty_release +}; + +static void test_xdh(struct reg *out, const struct reg *in, void *ctx) + { XDH(out[RZ].v.k, in[RX].v.k, in[RY].v.k); } + +static void test_xdhmct(struct reg *out, const struct reg *in, void *ctx) +{ + uint8_t b0[XDHOP(KEYSZ)], b1[XDHOP(KEYSZ)], *x = b0, *y = b1, *t; + unsigned long i, n; + + memcpy(b0, in[RX].v.k, sizeof(b0)); + memcpy(b1, in[RY].v.k, sizeof(b1)); + n = in[RN].v.u; + for (i = 0; i < n; i++) { + XDH(y, x, y); + t = y; y = x; x = t; + } + memcpy(out[RZ].v.k, x, sizeof(b0)); +} +#define REG_X { "x", RX, ®ty_key, 0 } +#define REG_Y { "Y", RY, ®ty_key, 0 } +#define REG_Z { "Z", RZ, ®ty_key, 0 } +#define REG_N { "n", RN, ®ty_uint, 0 } +static const struct regdef + xdh_regs[] = { REG_X, REG_Y, REG_Z, REGLIST_END }, + xdhmct_regs[] = { REG_X, REG_Y, REG_N, REG_Z, REGLIST_END }; + +static const struct test tests[] = { + { STRING(XDH), run_test, xdh_regs, test_xdh }, + { STRING(XDH) "-mct", run_test, xdhmct_regs, test_xdhmct }, + { 0 } +}; + +int main(void) + { return run_test_suite(NROUT, NREG, sizeof(struct reg), tests, stdin); } -- 2.11.0