Build and test the new crypto toys mdw/import-catacomb
authorMark Wooding <mdw@distorted.org.uk>
Sun, 29 Sep 2019 02:14:32 +0000 (03:14 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 29 Sep 2019 02:14:32 +0000 (03:14 +0100)
  * 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
    <mLib/bits.h> which does various low-level bit operations.  Provide
    a fake version, `fake-mLib-bits.h', which implements the necessary
    things in terms of C99 <stdint.h> 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 <mdw@distorted.org.uk>
13 files changed:
.gitignore
Makefile.in
crypto-test.c [new file with mode: 0644]
crypto-test.h [new file with mode: 0644]
ec-field-test.c [new file with mode: 0644]
ed25519-test.c [new file with mode: 0644]
ed448-test.c [new file with mode: 0644]
fake-mLib-bits.h [new file with mode: 0644]
keccak1600-test.c [new file with mode: 0644]
sha3-test.c [new file with mode: 0644]
u64.h
unaligned.h
xdh-test.c [new file with mode: 0644]

index bf9a085..381246c 100644 (file)
@@ -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
 
index 6bc02ef..40407b9 100644 (file)
@@ -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 (file)
index 0000000..4c7c2e3
--- /dev/null
@@ -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 <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.  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&REGF_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(&reg->v); def->ty->init(&reg->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&REGF_LIVE) any = 1;
+       else if (!miss && !(def->f&REGF_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(&reg->v); def->ty->init(&reg->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(&REG(in, def->i)->v);
+                   if (def->i < state->nrout)
+                       def->ty->release(&REG(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(&reg->v);
+               if (def->i < state->nrout) {
+                   reg = REG(out, def->i);
+                   reg->f = 0; def->ty->init(&reg->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&REGF_LIVE) bail("register `%s' already set", def->name);
+       def->ty->parse(&reg->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 (file)
index 0000000..807ae07
--- /dev/null
@@ -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 (file)
index 0000000..51b3e64
--- /dev/null
@@ -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 <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                                                    \
+    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&REGF_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, &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); }
diff --git a/ed25519-test.c b/ed25519-test.c
new file mode 100644 (file)
index 0000000..ce5b9ef
--- /dev/null
@@ -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 <stdio.h>
+
+#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, &regty_bytes, 0 }
+#define REG_BIGA { "A", RA, &regty_bytes, 0 }
+#define REG_PH { "ph", RPH, &regty_int, 0 }
+#define REG_CTX { "ctx", RCTX, &regty_bytes, 0 }
+#define REG_M { "m", RM, &regty_bytes, 0 }
+#define REG_SIGIN { "sig", RSIGIN, &regty_bytes, 0 }
+
+#define REG_SIGOUT { "sig", RSIGOUT, &regty_bytes, 0 }
+#define REG_AOUT { "A", RAOUT, &regty_bytes, 0 }
+#define REG_RC { "rc", RRC, &regty_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 (file)
index 0000000..b240ace
--- /dev/null
@@ -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 <stdio.h>
+
+#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, &regty_bytes, 0 }
+#define REG_BIGA { "A", RA, &regty_bytes, 0 }
+#define REG_PH { "ph", RPH, &regty_int, 0 }
+#define REG_CTX { "ctx", RCTX, &regty_bytes, 0 }
+#define REG_M { "m", RM, &regty_bytes, 0 }
+#define REG_SIGIN { "sig", RSIGIN, &regty_bytes, 0 }
+
+#define REG_SIGOUT { "sig", RSIGOUT, &regty_bytes, 0 }
+#define REG_AOUT { "A", RAOUT, &regty_bytes, 0 }
+#define REG_RC { "rc", RRC, &regty_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 (file)
index 0000000..e2e83ca
--- /dev/null
@@ -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 <mLib/bits.h> 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 <mLib/bits.h> interface, just
+ * enough to make the pieces of code lifted from Catacomb feel at home.
+ */
+
+#include <stdint.h>
+#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 (file)
index 0000000..e0102ac
--- /dev/null
@@ -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 <stdio.h>
+
+#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, &regty_bytes, 0 }
+#define REG_N { "n", RN, &regty_uint, 0 }
+#define REG_Z { "z", RZ, &regty_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 (file)
index 0000000..81d7610
--- /dev/null
@@ -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 <stdio.h>
+
+#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, &regty_bytes, 0 }
+#define REG_N { "n", RN, &regty_uint, 0 }
+#define REG_FUNC { "func", RFUNC, &regty_string, 0 }
+#define REG_PERSO { "perso", RPERSO, &regty_string, 0 }
+#define REG_H { "h", RH, &regty_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 (file)
--- 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)
index 2c48d0e..6f4f163 100644 (file)
@@ -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)
   (((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 (file)
index 0000000..08efabe
--- /dev/null
@@ -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 <stdio.h>
+
+#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, &regty_key, 0 }
+#define REG_Y { "Y", RY, &regty_key, 0 }
+#define REG_Z { "Z", RZ, &regty_key, 0 }
+#define REG_N { "n", RN, &regty_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); }