Test elliptic curves more thoroughly.
[u/mdw/catacomb] / ec-test.c
diff --git a/ec-test.c b/ec-test.c
new file mode 100644 (file)
index 0000000..6d42a70
--- /dev/null
+++ b/ec-test.c
@@ -0,0 +1,418 @@
+/* -*-c-*-
+ *
+ * $Id: ec-test.c,v 1.1 2004/03/23 15:19:32 mdw Exp $
+ *
+ * Code for testing elliptic-curve stuff
+ *
+ * (c) 2004 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb 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 Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: ec-test.c,v $
+ * Revision 1.1  2004/03/23 15:19:32  mdw
+ * Test elliptic curves more thoroughly.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/alloc.h>
+#include <mLib/testrig.h>
+#include <mLib/sub.h>
+
+#include "ec.h"
+#include "ec-test.h"
+
+/*----- Cardboard cut-out elliptic curve ----------------------------------*/
+
+typedef struct ecctx {
+  ec_curve c;
+  unsigned long magic;
+  char *name;
+  ec_curve *real;
+} ecctx;
+
+#define MAGIC 0x3a1f0b07
+
+static void ecDESTROY(ec_curve *cc)
+{
+  ecctx *c = (ecctx *)cc;
+  xfree(c->name);
+  ec_destroycurve(c->real);
+  DESTROY(c);
+}
+
+#define UNOP(OP)                                                       \
+  static ec *ec##OP(ec_curve *cc, ec *d, const ec *p) {                        \
+    ecctx *c = (ecctx *)cc;                                            \
+    return (EC_##OP(c->real, d, p));                                   \
+  }
+
+#define BINOP(OP)                                                      \
+  static ec *ec##OP(ec_curve *cc, ec *d, const ec *p, const ec *q) {   \
+    ecctx *c = (ecctx *)cc;                                            \
+    return (EC_##OP(c->real, d, p, q));                                        \
+  }
+
+UNOP(IN)
+UNOP(OUT)
+UNOP(FIX)
+UNOP(NEG)
+UNOP(DBL)
+BINOP(ADD)
+BINOP(SUB)
+
+#undef UNOP
+#undef BINOP
+
+static ec *ecFIND(ec_curve *cc, ec *d, mp *x)
+{
+  ecctx *c = (ecctx *)cc;
+  return (EC_FIND(c->real, d, x));
+}
+
+static int ecCHECK(ec_curve *cc, const ec *p)
+{
+  ecctx *c = (ecctx *)cc;
+  return (EC_CHECK(c->real, p));
+}
+
+static ec_ops ecops = {
+  ecDESTROY, ecIN, ecOUT, ecFIX,
+  ecFIND, ecNEG, ecADD, ecSUB, ecDBL, ecCHECK
+};
+
+static ec_curve *ec_cutout(ec_curve *real, const char *name)
+{
+  ecctx *c = CREATE(ecctx);
+  c->c.f = real->f;
+  c->c.ops = &ecops;
+  c->magic = MAGIC;
+  c->name = xstrdup(name);
+  c->real = real;
+  return (&c->c);
+}
+
+static const char *ec_name(ec_curve *cc)
+{
+  ecctx *c = (ecctx *)cc;
+  assert(c->magic == MAGIC);
+  return (c->name);
+}
+
+/*----- Test field types --------------------------------------------------*
+ *
+ * Really lazy parser.  Sorry.
+ */
+
+static void skipws(const char **p)
+{
+  while (isspace((unsigned char)**p)) (*p)++;
+}
+
+static void ckchar(const char **p, int ch)
+{ skipws(p); if (**p == ch) (*p)++; }
+
+static void ckend(const char **p)
+{
+  skipws(p);
+  if (**p) {
+    fprintf(stderr, "syntax error: junk at end of line\n");
+    abort();
+  }
+}
+
+static int ckstring(const char **p, const char **s)
+{
+  int i;
+  size_t n;
+
+  skipws(p);
+  for (i = 0; s[i]; i++) {
+    n = strlen(s[i]);
+    if (strncmp(*p, s[i], n) == 0 && !isalnum((unsigned char)(*p)[n])) {
+      *p += n;
+      return (i);
+    }
+  }
+  fprintf(stderr, "syntax error: couldn't recognize keyword\n");
+  abort();
+}
+
+static mp *getmp(const char **p)
+{
+  char *q;
+  mp *m;
+  skipws(p);
+  m = mp_readstring(MP_NEW, *p, &q, 0);
+  if (!m || isalnum((unsigned char)*q)) {
+    fprintf(stderr, "syntax error: bad number\n");
+    abort();
+  }
+  *p = q;
+  return (m);
+}
+
+static void ecvcvt(const char *buf, dstr *d)
+{
+  field *f;
+  ec_curve *v;
+  mp *m, *n;
+  const char *p = buf;
+  int i;
+
+  static const char *fnames[] = {
+    "prime", "binpoly", 0
+  };
+  static const char *ecnames[] = {
+    "prime", "primeproj", "bin", "binproj", 0
+  };
+
+  switch (i = ckstring(&p, fnames), ckchar(&p, ':'), i) {
+    case 0: m = getmp(&p); f = field_prime(m); mp_drop(m); break;
+    case 1: m = getmp(&p); f = field_binpoly(m); mp_drop(m); break;
+    default: abort();
+  }
+  ckchar(&p, '/');
+
+  switch (i = ckstring(&p, ecnames), ckchar(&p, ':'), i) {
+    case 0: m = getmp(&p); ckchar(&p, ','); n = getmp(&p);
+      v = ec_prime(f, m, n); mp_drop(m); mp_drop(n); break;
+    case 1: m = getmp(&p); ckchar(&p, ','); n = getmp(&p);
+      v = ec_primeproj(f, m, n); mp_drop(m); mp_drop(n); break;
+    case 2: m = getmp(&p); ckchar(&p, ','); n = getmp(&p);
+      v = ec_bin(f, m, n); mp_drop(m); mp_drop(n); break;
+    case 3: m = getmp(&p); ckchar(&p, ','); n = getmp(&p);
+      v = ec_binproj(f, m, n); mp_drop(m); mp_drop(n); break;
+    default: abort();
+  }
+  ckend(&p);
+
+  dstr_ensure(d, sizeof(v));
+  *(ec_curve **)d->buf = ec_cutout(v, buf);
+  d->len += sizeof(v);
+}
+
+static void ecvdump(dstr *d, FILE *fp)
+{
+  ec_curve *v = *(ec_curve **)d->buf;
+  fprintf(fp, "%s", ec_name(v));
+}
+
+test_type type_ecurve = { ecvcvt, ecvdump };
+
+static void eccvt(const char *p, dstr *d)
+{
+  ec *a;
+
+  dstr_ensure(d, sizeof(ec));
+  a = (ec *)d->buf;
+  d->len += sizeof(ec);
+  ec_create(a);
+  skipws(&p);
+  if (strcmp(p, "inf") == 0)
+    EC_SETINF(a);
+  else
+  { a->x = getmp(&p); ckchar(&p, ','); a->y = getmp(&p); ckend(&p); }
+}
+
+static void ecdodump(ec *a, FILE *fp)
+{
+  if (EC_ATINF(a))
+    fputs("inf", fp);
+  else {
+    fputs("0x", fp); 
+    mp_writefile(a->x, fp, 16);
+    fputs(", 0x", fp);
+    mp_writefile(a->y, fp, 16);
+  }
+}
+
+static void ecdump(dstr *d, FILE *fp)
+{
+  ec *a = (ec *)d->buf;
+  ecdodump(a, fp);
+}
+
+test_type type_ec = { eccvt, ecdump };
+
+/*----- Testing elliptic curve functionality ------------------------------*/
+
+#ifdef TEST_RIG
+
+static void ecdestroy(ec_curve *c)
+{
+  field *f = c->f;
+  ec_destroycurve(c);
+  F_DESTROY(f);
+}
+
+#define UNOP(op)                                                       \
+  static int v##op(dstr v[])                                           \
+  {                                                                    \
+    ec_curve *e = *(ec_curve **)v[0].buf;                              \
+    ec *a = (ec *)v[1].buf;                                            \
+    ec *r = (ec *)v[2].buf;                                            \
+    ec c = EC_INIT;                                                    \
+    int ok = 1;                                                                \
+    ec_##op(e, &c, a);                                                 \
+    if (!EC_EQ(r, &c)) {                                               \
+      fprintf(stderr, #op "failed");                                   \
+      fprintf(stderr, "\ncurve = "); type_ecurve.dump(v, stderr);      \
+      fprintf(stderr, "\n    a = "); ecdodump(a, stderr);              \
+      fprintf(stderr, "\n    r = "); ecdodump(r, stderr);              \
+      fprintf(stderr, "\n    c = "); ecdodump(&c, stderr);             \
+      fprintf(stderr, "\n");                                           \
+      ok = 0;                                                          \
+    }                                                                  \
+    EC_DESTROY(a); EC_DESTROY(r); EC_DESTROY(&c);                      \
+    ecdestroy(e);                                                      \
+    return (ok);                                                       \
+  }
+
+#define BINOP(op)                                                      \
+  static int v##op(dstr v[])                                           \
+  {                                                                    \
+    ec_curve *e = *(ec_curve **)v[0].buf;                              \
+    ec *a = (ec *)v[1].buf;                                            \
+    ec *b = (ec *)v[2].buf;                                            \
+    ec *r = (ec *)v[3].buf;                                            \
+    ec c = EC_INIT;                                                    \
+    int ok = 1;                                                                \
+    ec_##op(e, &c, a, b);                                              \
+    if (!EC_EQ(r, &c)) {                                               \
+      fprintf(stderr, #op "failed");                                   \
+      fprintf(stderr, "\ncurve = "); type_ecurve.dump(v, stderr);      \
+      fprintf(stderr, "\n    a = "); ecdodump(a, stderr);              \
+      fprintf(stderr, "\n    b = "); ecdodump(b, stderr);              \
+      fprintf(stderr, "\n    r = "); ecdodump(r, stderr);              \
+      fprintf(stderr, "\n    c = "); ecdodump(&c, stderr);             \
+      fprintf(stderr, "\n");                                           \
+      ok = 0;                                                          \
+    }                                                                  \
+    EC_DESTROY(a); EC_DESTROY(b); EC_DESTROY(r); EC_DESTROY(&c);       \
+    ecdestroy(e);                                                      \
+    return (ok);                                                       \
+  }
+
+UNOP(neg)
+UNOP(dbl)
+BINOP(add)
+BINOP(sub)
+
+static int vcheck(dstr v[])
+{
+  ec_curve *e = *(ec_curve **)v[0].buf;
+  ec *a = (ec *)v[1].buf;
+  int r = *(int *)v[2].buf;
+  int c;
+  int ok = 1;
+  c = ec_check(e, a);
+  if (r != c) {
+    fprintf(stderr, "check failed");
+    fprintf(stderr, "\ncurve = "); type_ecurve.dump(v, stderr);
+    fprintf(stderr, "\n    a = "); ecdodump(a, stderr);
+    fprintf(stderr, "\n    r = %d", r);
+    fprintf(stderr, "\n    c = %d", c); 
+    fprintf(stderr, "\n");
+    ok = 0;
+  }
+  EC_DESTROY(a);
+  ecdestroy(e);
+  return (ok);
+}
+
+static int vmul(dstr v[])
+{
+  ec_curve *e = *(ec_curve **)v[0].buf;
+  ec *a = (ec *)v[1].buf;
+  mp *n = *(mp **)v[2].buf;
+  ec *r = (ec *)v[3].buf;
+  ec c = EC_INIT;
+  int ok = 1;
+  ec_mul(e, &c, a, n);
+  if (!EC_EQ(r, &c)) {
+    fprintf(stderr, "mul failed");
+    fprintf(stderr, "\ncurve = "); type_ecurve.dump(v, stderr);
+    fprintf(stderr, "\n    a = "); ecdodump(a, stderr);
+    fprintf(stderr, "\n    n = "); mp_writefile(n, stderr, 10);
+    fprintf(stderr, "\n    r = "); ecdodump(r, stderr);
+    fprintf(stderr, "\n    c = "); ecdodump(&c, stderr);
+    fprintf(stderr, "\n");
+    ok = 0;
+  }
+  EC_DESTROY(a); EC_DESTROY(r); EC_DESTROY(&c); MP_DROP(n);
+  ecdestroy(e);
+  return (ok);
+}
+
+static int vfind(dstr v[])
+{
+  ec_curve *e = *(ec_curve **)v[0].buf;
+  mp *x = *(mp **)v[1].buf;
+  ec *r = (ec *)v[2].buf;
+  ec c = EC_INIT;
+  int ok = 1;
+  if (!ec_find(e, &c, x)) EC_SETINF(&c);
+  if (!EC_EQ(r, &c)) {
+    fprintf(stderr, "find failed");
+    fprintf(stderr, "\ncurve = "); type_ecurve.dump(v, stderr);
+    fprintf(stderr, "\n    x = "); mp_writefile(x, stderr, 16);
+    fprintf(stderr, "\n    r = "); ecdodump(r, stderr);
+    fprintf(stderr, "\n    c = "); ecdodump(&c, stderr);
+    fprintf(stderr, "\n");
+    ok = 0;
+  }
+  MP_DROP(x); EC_DESTROY(r); EC_DESTROY(&c);
+  ecdestroy(e);
+  return (ok);
+}
+
+static test_chunk tests[] = {
+  { "neg", vneg, { &type_ecurve, &type_ec, &type_ec } },
+  { "dbl", vdbl, { &type_ecurve, &type_ec, &type_ec } },
+  { "add", vadd, { &type_ecurve, &type_ec, &type_ec, &type_ec } },
+  { "sub", vsub, { &type_ecurve, &type_ec, &type_ec, &type_ec } },
+  { "mul", vmul, { &type_ecurve, &type_ec, &type_mp, &type_ec } },
+  { "check", vcheck, { &type_ecurve, &type_ec, &type_int } },
+  { "find", vfind, { &type_ecurve, &type_mp, &type_ec } },
+  { 0, 0, { 0 } }
+};
+
+int main(int argc, char *argv[])
+{
+  sub_init();
+  test_run(argc, argv, tests, SRCDIR "/tests/ec");
+  return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/