progs/perftest.c: Use from Glibc syscall numbers.
[catacomb] / progs / rspit.c
index 7ca1cce..812525f 100644 (file)
@@ -27,6 +27,8 @@
 
 /*----- Header files ------------------------------------------------------*/
 
+#define _FILE_OFFSET_BITS 64
+
 #include "config.h"
 
 #include <assert.h>
 
 #ifndef PORTABLE
 #  include <unistd.h>
+#  include <sys/time.h>
 #endif
 
 #include <mLib/darray.h>
 #include <mLib/dstr.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
 #include <mLib/sub.h>
 
+#include "mp.h"
+#include "mpint.h"
+#include "mplimits.h"
+#include "mptext.h"
+
 #include "fipstest.h"
 #include "grand.h"
 #include "maurer.h"
 #include "bbs.h"
 #include "mprand.h"
 
+#include "chacha.h"
 #include "rc4.h"
+#include "salsa20.h"
+#include "salsa20-core.h"
 #include "seal.h"
+#include "sha3.h"
 
 #include "des-ofb.h"
 #include "des3-ofb.h"
@@ -197,10 +210,66 @@ static const struct {
 #undef E
 };
 
+#define SALSAE                                                         \
+  E(salsa20, 20,, SALSA20)                                             \
+  E(salsa20, 12,, SALSA20)                                             \
+  E(salsa20, 8,, SALSA20)                                              \
+  E(xsalsa20, 20, X, SALSA20)                                          \
+  E(xsalsa20, 12, X, SALSA20)                                          \
+  E(xsalsa20, 8, X, SALSA20)                                           \
+  E(chacha, 20,, CHACHA)                                               \
+  E(chacha, 12,, CHACHA)                                               \
+  E(chacha, 8,, CHACHA)                                                        \
+  E(xchacha, 20, X, CHACHA)                                            \
+  E(xchacha, 12, X, CHACHA)                                            \
+  E(xchacha, 8, X, CHACHA)
+
+#define E(pre, r, x, BASE) pre##_##r##_INDEX,
+enum { SALSAE BOGUS_SALSA };
+#undef E
+
+#define SALSA20_GEN(pre, r) SALSA20_DECOR(pre, r, _rand)
+#define CHACHA_GEN(pre, r) pre##r##_rand
+
+#define SALSA20_NAME(r) SALSA20_NAME_##r
+#define XSALSA20_NAME(r) "x" SALSA20_NAME_##r
+#define CHACHA_NAME(r) "chacha" #r
+#define XCHACHA_NAME(r) "xchacha" #r
+
+static const struct {
+  size_t noncesz;
+  grand *(*gen)(const void *, size_t, const void *);
+} salsatab[] = {
+#define E(pre, r, x, BASE) { x##BASE##_NONCESZ, BASE##_GEN(pre, r) },
+  SALSAE
+#undef E
+};
+
+#define SHAKES E(128) E(256)
+
+enum {
+#define E(sz) SHAKE##sz##_INDEX,
+  SHAKES
+#undef E
+  SHAKE__LIMIT
+};
+
+static const struct {
+  const octet *ksz;
+  grand *(*shake)(const void *, size_t, const void *, size_t,
+                 const void *, size_t);
+  grand *(*kmac)(const void *, size_t,
+                const void *, size_t);
+} shaketab[] = {
+#define E(sz) { shake##sz##_keysz, cshake##sz##_rand, kmac##sz##_rand },
+  SHAKES
+#undef E
+};
+
 /*----- Miscellaneous static data -----------------------------------------*/
 
 static FILE *outfp;
-static size_t outsz = 0;
+static mp *outsz = 0;
 static unsigned maurer_lo = 5, maurer_hi = 8;
 
 static int argc;
@@ -367,7 +436,7 @@ static int opt(void)
       case 'o':
        if (flags & f_file)
          die(EXIT_FAILURE, "already set an output file");
-       if (strcmp(optarg, "-") == 0)
+       if (STRCMP(optarg, ==, "-"))
          outfp = stdout;
        else {
          outfp = fopen(optarg, "w");
@@ -380,13 +449,13 @@ static int opt(void)
        break;
       case 'z': {
        char *p;
-       outsz = strtoul(optarg, &p, 0);
-       if (!outsz)
+       outsz = mp_readstring(outsz, optarg, &p, 0);
+       if (!outsz || MP_NEGP(outsz))
          die(EXIT_FAILURE, "bad number `%s'", optarg);
        switch (*p) {
-         case 'G': case 'g': outsz *= 1024;
-         case 'M': case 'm': outsz *= 1024;
-         case 'K': case 'k': outsz *= 1024;
+         case 'G': case 'g': outsz = mp_lsl(outsz, outsz, 10);
+         case 'M': case 'm': outsz = mp_lsl(outsz, outsz, 10);
+         case 'K': case 'k': outsz = mp_lsl(outsz, outsz, 10);
          case 0:
            break;
          default:
@@ -799,6 +868,75 @@ static grand *gen_seal(unsigned i)
   return (r);
 }
 
+/* --- Salsa20, XSalsa20, ChaCha, and XChaCha --- */
+
+static grand *gen_salsae(unsigned i)
+{
+  grand *r;
+  char *p;
+  dstr d = DSTR_INIT;
+  dstr n = DSTR_INIT;
+  kludge64 pos = { 0 };
+  octet posbuf[8];
+  mp *x;
+
+  static struct option opts[] = {
+    { "key",           OPTF_ARGREQ,    0,      'k' },
+    { "hex",           OPTF_ARGREQ,    0,      'H' },
+    { "nonce",         OPTF_ARGREQ,    0,      'n' },
+    { "seek",          OPTF_ARGREQ,    0,      's' },
+    { 0,               0,              0,      0 }
+  };
+
+  addopts("k:H:n:s:", opts);
+
+  for (;;) {
+    int o = opt();
+    if (o < 0)
+      break;
+    switch (o) {
+      case 'k':
+       DRESET(&d);
+       textkey(&d, optarg, salsa20_keysz);
+       break;
+      case 'H':
+       DRESET(&d);
+       hexkey(&d, optarg, salsa20_keysz);
+       break;
+      case 'n':
+       DRESET(&n);
+       unhex(optarg, &p, &n);
+       if (*p)
+         die(EXIT_FAILURE, "bad hex IV `%s'", optarg);
+       if (n.len != salsatab[i].noncesz) {
+         die(EXIT_FAILURE, "bad nonce length %lu (must be %lu)",
+             (unsigned long)n.len, (unsigned long)salsatab[i].noncesz);
+       }
+       break;
+      case 's':
+       x = mp_readstring(MP_NEW, optarg, &p, 0);
+       if (*p || MP_NEGP(x) || mp_bits(x) > 64)
+         die(EXIT_FAILURE, "bad position `%s'", optarg);
+       mp_storeb(x, posbuf, sizeof(posbuf));
+       mp_drop(x);
+       LOAD64_(pos, posbuf);
+       break;
+      default:
+       return (0);
+    }
+  }
+
+  if (!d.len)
+    randkey(&d, salsa20_keysz);
+  r = salsatab[i].gen(d.buf, d.len, n.len ? n.buf : 0);
+  r->ops->misc(r, SALSA20_SEEKU64, pos);
+
+  dstr_destroy(&d);
+  dstr_destroy(&n);
+
+  return (r);
+}
+
 /* --- Output feedback generators --- */
 
 static grand *gen_ofb(unsigned i)
@@ -831,9 +969,14 @@ static grand *gen_ofb(unsigned i)
        break;
       case 'i': {
        char *p;
+       DRESET(&iv);
        unhex(optarg, &p, &iv);
        if (*p)
          die(EXIT_FAILURE, "bad hex IV `%s'", optarg);
+       if (iv.len != ciphertab[i].blksz) {
+         die(EXIT_FAILURE, "bad IV length %lu (must be %lu)",
+             (unsigned long)iv.len, (unsigned long)ciphertab[i].blksz);
+       }
       } break;
       default:
        return (0);
@@ -843,13 +986,8 @@ static grand *gen_ofb(unsigned i)
   if (!d.len)
     randkey(&d, ciphertab[i].keysz);
   r = ciphertab[i].ofb(d.buf, d.len);
-  if (iv.len) {
-    if (iv.len != ciphertab[i].blksz) {
-      die(EXIT_FAILURE, "bad IV length %lu (must be %lu)",
-         (unsigned long)iv.len, (unsigned long)ciphertab[i].blksz);
-    }
+  if (iv.len)
     r->ops->misc(r, GRAND_SEEDBLOCK, iv.buf);
-  }
 
   dstr_destroy(&d);
   dstr_destroy(&iv);
@@ -888,9 +1026,14 @@ static grand *gen_counter(unsigned i)
        break;
       case 'i': {
        char *p;
+       DRESET(&iv);
        unhex(optarg, &p, &iv);
        if (*p)
          die(EXIT_FAILURE, "bad hex IV `%s'", optarg);
+       if (iv.len != ciphertab[i].blksz) {
+         die(EXIT_FAILURE, "bad IV length %lu (must be %lu)",
+             (unsigned long)iv.len, (unsigned long)ciphertab[i].blksz);
+       }
       } break;
       default:
        return (0);
@@ -900,13 +1043,8 @@ static grand *gen_counter(unsigned i)
   if (!d.len)
     randkey(&d, ciphertab[i].keysz);
   r = ciphertab[i].counter(d.buf, d.len);
-  if (iv.len) {
-    if (iv.len != ciphertab[i].blksz) {
-      die(EXIT_FAILURE, "bad IV length %lu (must be %lu)",
-         (unsigned long)iv.len, (unsigned long)ciphertab[i].blksz);
-    }
+  if (iv.len)
     r->ops->misc(r, GRAND_SEEDBLOCK, iv.buf);
-  }
 
   dstr_destroy(&d);
   dstr_destroy(&iv);
@@ -965,6 +1103,113 @@ static grand *gen_mgf(unsigned i)
   return (r);
 }
 
+/* --- SHAKE generators --- */
+
+static grand *gen_shake(unsigned i)
+{
+  dstr d = DSTR_INIT;
+  const char *func = 0, *perso = 0;
+  grand *r;
+
+  static struct option opts[] = {
+    { "function",      OPTF_ARGREQ,    0,      'F' },
+    { "personalization", OPTF_ARGREQ,  0,      'P' },
+    { "key",           OPTF_ARGREQ,    0,      'k' },
+    { "hex",           OPTF_ARGREQ,    0,      'H' },
+    { 0,               0,              0,      0 }
+  };
+
+  addopts("F:P:k:H:", opts);
+
+  for (;;) {
+    int o = opt();
+    if (o < 0)
+      break;
+    switch (o) {
+      case 'F':
+       func = optarg;
+       break;
+      case 'P':
+       perso = optarg;
+       break;
+      case 'k':
+       DRESET(&d);
+       textkey(&d, optarg, shaketab[i].ksz);
+       break;
+      case 'H':
+       DRESET(&d);
+       hexkey(&d, optarg, shaketab[i].ksz);
+       break;
+      default:
+       return (0);
+    }
+  }
+
+  if (!d.len) randkey(&d, shaketab[i].ksz);
+  r = shaketab[i].shake(func, func ? strlen(func) : 0,
+                       perso, perso ? strlen(perso) : 0,
+                       d.buf, d.len);
+  dstr_destroy(&d);
+  return (r);
+}
+
+/* --- KMAC generators --- */
+
+static grand *gen_kmac(unsigned i)
+{
+  dstr d = DSTR_INIT, m = DSTR_INIT;
+  const char *perso = 0;
+  char *q;
+  grand *r;
+
+  static struct option opts[] = {
+    { "personalization", OPTF_ARGREQ,  0,      'P' },
+    { "key",           OPTF_ARGREQ,    0,      'k' },
+    { "hex",           OPTF_ARGREQ,    0,      'H' },
+    { "message",       OPTF_ARGREQ,    0,      'M' },
+    { "msghex",                OPTF_ARGREQ,    0,      'N' },
+    { 0,               0,              0,      0 }
+  };
+
+  addopts("P:k:H:M:N:", opts);
+
+  for (;;) {
+    int o = opt();
+    if (o < 0)
+      break;
+    switch (o) {
+      case 'P':
+       perso = optarg;
+       break;
+      case 'k':
+       DRESET(&d);
+       textkey(&d, optarg, shaketab[i].ksz);
+       break;
+      case 'H':
+       DRESET(&d);
+       hexkey(&d, optarg, shaketab[i].ksz);
+       break;
+      case 'M':
+       DRESET(&m);
+       DPUTS(&d, optarg);
+       break;
+      case 'N':
+       DRESET(&m);
+       unhex(optarg, &q, &m);
+       if (*q) die(EXIT_FAILURE, "bad hex");
+       break;
+      default:
+       return (0);
+    }
+  }
+
+  if (!d.len) randkey(&d, shaketab[i].ksz);
+  r = shaketab[i].kmac(perso, perso ? strlen(perso) : 0, d.buf, d.len);
+  r->ops->misc(r, GRAND_SEEDBLOCK, (void *)m.buf, m.len);
+  dstr_destroy(&d); dstr_destroy(&m);
+  return (r);
+}
+
 /* --- Fibonacci generator --- */
 
 static grand *gen_fib(unsigned i)
@@ -1071,6 +1316,21 @@ gen generators[] = {
     "[-k KEY-PHRASE] [-H HEX-KEY] [-i INDEX]" },
   HASHES
 #undef E
+#define E(pre, r, x, BASE)                                             \
+  { x##BASE##_NAME(r), gen_salsae,     pre##_##r##_INDEX,              \
+    "[-k KEY-PHRASE] [-H HEX-KEY] [-n NONCE]" },
+  SALSAE
+#undef E
+#define E(sz)                                                          \
+  { "shake" #sz,       gen_shake,      SHAKE##sz##_INDEX,              \
+    "[-k KEY-PHRASE] [-H HEX-KEY]" },
+  SHAKES
+#undef E
+#define E(sz)                                                          \
+  { "kmac" #sz,                gen_kmac,       SHAKE##sz##_INDEX,              \
+    "[-k KEY-PHRASE] [-H HEX-KEY] [-m MSG]" },
+  SHAKES
+#undef E
   { "rc4",             gen_rc4,        0,
     "[-k KEY-PHRASE] [-H HEX-KEY]" },
   { "seal",            gen_seal,       0,
@@ -1119,15 +1379,32 @@ static int genmaurer(const void *buf, size_t sz, void *p)
   return (0);
 }
 
-static int generate(grand *r, size_t outsz,
+static double doubletime(void)
+{
+#ifdef PORTABLE
+  static time_t start = (time_t)-1;
+  time_t now = time(0);
+
+  if (start == (time_t)-1) start = now;
+  return difftime(now, start);
+#else
+  struct timeval tv;
+
+  gettimeofday(&tv, 0);
+  return (tv.tv_sec + tv.tv_usec/1000000.0);
+#endif
+}
+
+static int generate(grand *r, mp *outsz,
                    int (*func)(const void *buf, size_t sz, void *p),
                    void *p)
 {
   static char kmg[] = { ' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 0 };
 
   unsigned percent = 0;
-  size_t kb = 0;
-  time_t last;
+  mp *kb = MP_ZERO, *t = MP_NEW;
+  dstr d = DSTR_INIT;
+  double now, last;
   static char baton[] = "-\\|/";
   char *bp;
   int rc;
@@ -1135,7 +1412,7 @@ static int generate(grand *r, size_t outsz,
 
   /* --- Spit out random data --- */
 
-  last = time(0);
+  last = doubletime();
   bp = baton;
   if (flags & f_progress) {
     char *errbuf = xmalloc(BUFSIZ);
@@ -1151,16 +1428,20 @@ static int generate(grand *r, size_t outsz,
   signal(SIGPIPE, SIG_IGN);
 #endif
 
-  do {
+  while (!outsz || MP_CMP(kb, <, outsz)) {
     octet buf[BUFSIZ];
-    size_t sz = sizeof(buf);
+    size_t sz = sizeof(buf), left;
     clock_t c_start, c_stop;
 
     /* --- Emit a bufferful (or less) of data --- */
 
     if (outsz) {
-      if (sz > outsz - kb)
-       sz = outsz - kb;
+      t = mp_sub(t, outsz, kb);
+      assert(!MP_NEGP(t));
+      if (MP_CMP(t, <=, MP_SIZET_MAX)) {
+       left = mp_tosizet(t);
+       if (sz > left) sz = left;
+      }
     }
     c_start = clock();
     r->ops->fill(r, buf, sz);
@@ -1168,26 +1449,33 @@ static int generate(grand *r, size_t outsz,
     clk += c_stop - c_start;
     if (func && (rc = func(buf, sz, p)) != 0)
       return (rc);
-    kb += sz;
+    t = mp_fromsizet(t, sz);
+    kb = mp_add(kb, kb, t);
 
     /* --- Update the display --- */
 
     if (flags & f_progress) {
-      time_t t = time(0);
       unsigned up = 0;
 
+      now = doubletime();
+
       if (percent > 100)
        up = 1;
 
       if (!outsz) {
-       if (difftime(t, last) > 1.0) {
+       if (now - last > 0.1) {
          up = 1;
        }
        if (up)
          fputs(" ] ", stderr);
       } else {
-       unsigned pc = kb * 100.0 / outsz;
-       if (pc > percent || percent > 100 || difftime(t, last) > 1.0) {
+       unsigned pc;
+       t = mp_fromulong(t, 100);
+       t = mp_mul(t, t, kb);
+       mp_div(&t, 0, t, outsz);
+       assert(!MP_NEGP(t) && MP_CMP(t, <, MP_UINT_MAX));
+       pc = mp_touint(t);
+       if (pc > percent || percent > 100 || now - last > 0.1) {
          if (percent > 100)
            percent = 0;
          percent &= ~1;
@@ -1202,25 +1490,27 @@ static int generate(grand *r, size_t outsz,
       }
 
       if (up) {
-       size_t q = kb;
        char *kk = kmg;
-       while (q > 8192 && kk[1]) {
-         q >>= 10;
+       t = mp_add(t, kb, MP_ZERO);
+       while (mp_bits(t) >= 14) {
+         t = mp_lsr(t, t, 10);
          kk++;
        }
-       fprintf(stderr, "%4i%c\r[", (int)q, *kk);
+       DRESET(&d);
+       mp_writedstr(t, &d, 10);
+       fprintf(stderr, "%4s%c\r[", d.buf, *kk);
        if (outsz) {
          unsigned pc;
          for (pc = 0; pc < (percent & ~1); pc += 2)
            putc('.', stderr);
        }
-       last = t;
+       last = now;
       }
 
       if (percent > 100)
        percent = 0;
 
-      if (percent < 100) {
+      if (percent < 100 && up) {
        putc(*bp++, stderr);
        putc('\b', stderr);
        if (!*bp)
@@ -1228,26 +1518,44 @@ static int generate(grand *r, size_t outsz,
       }
       fflush(stderr);
     }
-
-    /* --- Terminate the loop --- */
-
-  } while (!outsz || kb < outsz);
+  }
 
   if (flags & f_progress)
     fputc('\n', stderr);
   if (flags & f_timer) {
-    fprintf(stderr, "generated %lu bytes ", (unsigned long)outsz);
+    DRESET(&d);
+    dstr_puts(&d, "generated ");
+    mp_writedstr(kb, &d, 10);
+    dstr_puts(&d, " bytes ");
     if (!clk)
-      fputs("too quickly to measure\n", stderr);
+      dstr_puts(&d, "too quickly to measure\n");
     else {
       char *kk;
+      double out;
       double sec = (double)clk/CLOCKS_PER_SEC;
-      double bps = (outsz << 3)/sec;
+      unsigned long sh;
+      double bps;
+
+      MP_SHRINK(kb);
+      switch (MP_LEN(kb)) {
+       case 0: out = 0; break;
+       case 1: out = kb->v[0]; break;
+       default:
+         sh = mp_bits(kb) - MPW_BITS;
+         t = mp_lsr(t, kb, sh);
+         out = ldexp(t->v[0], sh);
+         break;
+      }
+      bps = (8*out)/sec;
       for (kk = kmg; bps > 1024 && kk[1]; kk++, bps /= 1024)
        ;
-      fprintf(stderr, "in %g secs (%g %cb/s)\n", sec, bps, *kk);
+      dstr_putf(&d, "in %g secs (%g %cb/s)\n", sec, bps, *kk);
+      fwrite(d.buf, 1, d.len, stderr);
     }
   }
+
+  mp_drop(t);
+  DDESTROY(&d);
   return (0);
 }
 
@@ -1284,7 +1592,7 @@ int main(int ac, char *av[])
 
     g = 0;
     for (gg = generators; gg->name; gg++) {
-      if (strncmp(arg, gg->name, sz) == 0) {
+      if (STRNCMP(arg, ==, gg->name, sz)) {
        if (gg->name[sz] == 0) {
          g = gg;
          break;
@@ -1313,9 +1621,12 @@ int main(int ac, char *av[])
   if (flags & f_fips) {
     octet buf[FIPSTEST_BUFSZ];
     unsigned rc;
+    mp *t;
     octet *p = buf;
 
-    generate(r, sizeof(buf), genbuf, &p);
+    t = mp_fromsizet(MP_NEW, sizeof(buf));
+    generate(r, t, genbuf, &p);
+    mp_drop(t);
     rc = fipstest(buf);
     if (rc & FIPSTEST_MONOBIT)
       moan("failed monobit test");
@@ -1336,6 +1647,7 @@ int main(int ac, char *av[])
     size_t bufsz;
     unsigned i;
     unsigned rc = 0;
+    mp *t;
     genmaurer_ctx g;
 
     static struct { double x; const char *sig; } sigtab[] = {
@@ -1352,7 +1664,9 @@ int main(int ac, char *av[])
       maurer_init(&g.m[i], i + maurer_lo);
     bufsz = (100 * maurer_hi) << maurer_hi;
 
-    generate(r, bufsz, genmaurer, &g);
+    t = mp_fromsizet(MP_NEW, bufsz);
+    generate(r, t, genmaurer, &g);
+    mp_drop(t);
 
     for (i = maurer_lo; i <= maurer_hi; i++) {
       double z = maurer_done(&g.m[i - maurer_lo]);