Merge branch 'master' of git.distorted.org.uk:~mdw/publish/public-git/catacomb
authorMark Wooding <mdw@distorted.org.uk>
Mon, 13 Jun 2016 21:00:00 +0000 (22:00 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Mon, 13 Jun 2016 21:00:00 +0000 (22:00 +0100)
* 'master' of git.distorted.org.uk:~mdw/publish/public-git/catacomb: (53 commits)
  rand/rand.c (rdrand_quick): Improve the loop.
  configure.ac, rand/noise.c: Get high-res time from `clock_gettime'.
  rand/noise.c: Make the high-res timer function be a bit more abstract.
  configure.ac, Makefile.am: Collect libs only needed by Catcomb itself.
  Makefile.am: Include $(PIXIE_LIBS) in the main library.
  rand/noise.c (noise_devrandom): Use OpenBSD system call `getentropy'.
  rand/noise.c (noise_devrandom): Use new Linux system call `getrandom'.
  rand/noise.c (noise_devrandom): Handle Linux's broken `/dev/urandom'.
  rand/noise.c (noise_devrandom): Refactor internals.
  src/noise.c: Make `bitcount' table be static and constant.
  rand/rand.c: Add support for x86 `RDRAND' instruction in `rand_quick'.
  base/dispatch.[ch]: Detect availability of the x86 `RDRAND' instruction.
  rand/rand.[ch]: Add external `rand_quick' function.
  rand/: Secure `rand' generator against fork problems.
  Release 2.2.2.
  math/mp-arith.c (mp_testbit): Want nonstrict comparison for bounds check.
  symm/salsa20-arm-neon.S: Mark the final-permutation stores as word-aligned.
  symm/: Add ARM NEON implementations of ChaCha and Salsa20.
  symm/{salsa20,chacha}-x86ish-sse2.S: Use numeric labels for internal loops.
  symm/salsa20-x86ish-sse2.S: Fix stray `##' comment to be `//'.
  ...

Conflicts:
debian/changelog

48 files changed:
Makefile.am
base/Makefile.am
base/asm-common.h [new file with mode: 0644]
base/dispatch.c [new file with mode: 0644]
base/dispatch.h [new file with mode: 0644]
build-setup
configure.ac
debian/changelog
debian/rules
math/Makefile.am
math/mp-arith.c
math/mp.h
misc/Makefile.am
pub/Makefile.am
pub/bbs-rand.c
rand/Makefile.am
rand/dsarand.c
rand/grand.c
rand/grand.h
rand/lcrand.c
rand/noise.c
rand/rand.c
rand/rand.h
rand/randgen.c [new file with mode: 0644]
rand/sslprf.c
rand/tlsprf.c
symm/Makefile.am
symm/cbc-def.h
symm/cfb-def.h
symm/chacha-arm-neon.S [new file with mode: 0644]
symm/chacha-core.h
symm/chacha-x86ish-sse2.S [new file with mode: 0644]
symm/chacha.c
symm/counter-def.h
symm/ecb-def.h
symm/mgf-def.h
symm/modes.am.in
symm/ofb-def.h
symm/rc4.c
symm/rijndael-base.c
symm/rijndael-x86ish-aesni.S [new file with mode: 0644]
symm/rijndael.c
symm/salsa20-arm-neon.S [new file with mode: 0644]
symm/salsa20-core.h
symm/salsa20-x86ish-sse2.S [new file with mode: 0644]
symm/salsa20.c
symm/seal.c
vars.am

index 777a39d..f6fa934 100644 (file)
@@ -33,8 +33,10 @@ SUBDIRS                       =
 ### The main library.
 
 lib_LTLIBRARIES                 = libcatacomb.la
-libcatacomb_la_LDFLAGS  = -version-info $(LIBTOOL_VERSION_INFO)
-libcatacomb_la_LIBADD   = $(MATHLIBS) $(mLib_LIBS)
+libcatacomb_la_LDFLAGS  = -version-info $(LIBTOOL_VERSION_INFO) \
+                               -no-undefined
+libcatacomb_la_LIBADD   = $(MATHLIBS) $(PIXIE_LIBS) $(CATACOMB_LIBS) \
+                               $(mLib_LIBS)
 libcatacomb_la_SOURCES  =
 
 ## Basic utilities.
index 35c86ff..0ac43f2 100644 (file)
@@ -40,6 +40,10 @@ libbase_la_SOURCES   += arena.c
 pkginclude_HEADERS     += ct.h
 libbase_la_SOURCES     += ct.c
 
+## CPU-specific dispatch.
+pkginclude_HEADERS     += dispatch.h
+libbase_la_SOURCES     += dispatch.c
+
 ## Acceptable key-size descriptions.
 pkginclude_HEADERS     += keysz.h
 libbase_la_SOURCES     += keysz.c keysz-conv.c
@@ -51,4 +55,7 @@ libbase_la_SOURCES    += lmem.c
 ## Clearing secrets from memory.
 pkginclude_HEADERS     += paranoia.h
 
+## Base definitions for assembler source.
+EXTRA_DIST             += asm-common.h
+
 ###----- That's all, folks --------------------------------------------------
diff --git a/base/asm-common.h b/base/asm-common.h
new file mode 100644 (file)
index 0000000..0af9cb5
--- /dev/null
@@ -0,0 +1,221 @@
+/// -*- mode: asm; asm-comment-char: ?/ -*-
+///
+/// Fancy SIMD implementation of Salsa20
+///
+/// (c) 2015 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.
+
+///--------------------------------------------------------------------------
+/// General definitions.
+
+// Announcing an external function.
+#define FUNC(name)                                                     \
+       .globl  F(name);                                                \
+       TYPE_FUNC(name);                                                \
+       .macro  ENDFUNC; _ENDFUNC(name); .endm;                         \
+       FUNC_PREHOOK(name);                                             \
+F(name):                                                               \
+       FUNC_POSTHOOK(name)
+
+// Marking the end of a function.
+#define _ENDFUNC(name)                                                 \
+       .purgem ENDFUNC;                                                \
+       SIZE_OBJ(name);                                                 \
+       ENDFUNC_HOOK(name)
+
+///--------------------------------------------------------------------------
+/// ELF-specific hacking.
+
+#if __ELF__
+
+#if __PIC__ || __PIE__
+#  define WANT_PIC 1
+#endif
+
+#define TYPE_FUNC(name) .type name, STT_FUNC
+
+#define SIZE_OBJ(name) .size name, . - name
+
+#endif
+
+///--------------------------------------------------------------------------
+/// Windows-specific hacking.
+
+#if ABI_WIN
+
+#if CPUFAM_X86
+#  define F(name) _##name
+#endif
+
+#endif
+
+///--------------------------------------------------------------------------
+/// x86- and amd64-specific hacking.
+///
+/// It's (slightly) easier to deal with both of these in one go.
+
+#if CPUFAM_X86 || CPUFAM_AMD64
+
+// Set the function hooks.
+#define FUNC_PREHOOK(_) .balign 16
+
+// Don't use the wretched AT&T syntax.  It's festooned with pointless
+// punctuation, and all of the data movement is backwards.  Ugh!
+       .intel_syntax noprefix
+
+// Call external subroutine at ADDR, possibly via PLT.
+       .macro  callext addr
+#if WANT_PIC
+       call    \addr@PLT
+#else
+       call    \addr
+#endif
+       .endm
+
+// Do I need to arrange a spare GOT register?
+#if WANT_PIC && CPUFAM_X86
+#  define NEED_GOT 1
+#endif
+#define GOTREG ebx                     // Not needed in AMD64 so don't care.
+
+// Maybe load GOT address into GOT.
+       .macro  ldgot got=GOTREG
+#if WANT_PIC && CPUFAM_X86
+       call    _where_am_i.\got
+       add     \got, offset _GLOBAL_OFFSET_TABLE_
+#endif
+       .endm
+
+// Maybe build a helper subroutine for `ldgot GOT'.
+       .macro  gotaux got=GOTREG
+#if WANT_PIC && CPUFAM_X86
+       .align  16
+_where_am_i.\got :
+       mov     \got, [esp]
+       ret
+#endif
+       .endm
+
+// Load address of external symbol ADDR into REG, maybe using GOT.
+       .macro  leaext reg, addr, got=GOTREG
+#if WANT_PIC
+#  if CPUFAM_X86
+       mov     \reg, [\got + \addr@GOT]
+#  endif
+#  if CPUFAM_AMD64
+       mov     \reg, \addr@GOTPCREL[rip]
+#  endif
+#else
+#  if CPUFAM_X86
+       mov     \reg, offset \addr
+#  endif
+#  if CPUFAM_AMD64
+       lea     \reg, \addr[rip]
+#  endif
+#endif
+       .endm
+
+// Address expression (possibly using a base register, and a displacement)
+// referring to ADDR, which is within our module, maybe using GOT.
+#define INTADDR(...) INTADDR__0(__VA_ARGS__, GOTREG, dummy)
+#define INTADDR__0(addr, got, ...) INTADDR__1(addr, got)
+#if CPUFAM_AMD64
+#  define INTADDR__1(addr, got) addr + rip
+#elif WANT_PIC
+#  define INTADDR__1(addr, got) got + addr@GOTOFF
+#else
+#  define INTADDR__1(addr, got) addr
+#endif
+
+#endif
+
+///--------------------------------------------------------------------------
+/// ARM-specific hacking.
+
+#if CPUFAM_ARM
+
+// Set the function hooks.
+#define FUNC_PREHOOK(_) .balign 4
+#define ENDFUNC_HOOK(name) .ltorg
+
+// Call external subroutine at ADDR, possibly via PLT.
+       .macro  callext addr, cond=
+#if WANT_PIC
+       bl\cond \addr(PLT)
+#else
+       bl\cond \addr
+#endif
+       .endm
+
+// Do I need to arrange a spare GOT register?
+#if WANT_PIC
+#  define NEED_GOT 1
+#endif
+#define GOTREG r9
+
+// Maybe load GOT address into GOT.
+       .macro  ldgot   got=r9
+#if WANT_PIC
+       ldr     \got, =_GLOBAL_OFFSET_TABLE_ - . - 12
+       add     \got, pc, \got
+#endif
+       .endm
+
+// Load address of external symbol ADDR into REG, maybe using GOT.
+       .macro  leaext  reg, addr, cond=, got=GOTREG
+#if WANT_PIC
+       ldr     \reg, =\addr(GOT)
+       ldr     \reg, [\got, \reg]
+#else
+       ldr     \reg, =\addr
+#endif
+       .endm
+
+#endif
+
+///--------------------------------------------------------------------------
+/// Final stuff.
+
+// Default values for the various hooks.
+#ifndef FUNC_PREHOOK
+#  define FUNC_PREHOOK(name)
+#endif
+#ifndef FUNC_POSTHOOK
+#  define FUNC_POSTHOOK(name)
+#endif
+#ifndef ENDFUNC_HOOK
+#  define ENDFUNC_HOOK(name)
+#endif
+
+#ifndef F
+#  define F(name) name
+#endif
+
+#ifndef TYPE_FUNC
+#  define TYPE_FUNC(name)
+#endif
+
+#ifndef SIZE_OBJ
+#  define SIZE_OBJ(name)
+#endif
+
+///----- That's all, folks --------------------------------------------------
diff --git a/base/dispatch.c b/base/dispatch.c
new file mode 100644 (file)
index 0000000..51619d5
--- /dev/null
@@ -0,0 +1,536 @@
+/* -*-c-*-
+ *
+ * CPU-specific dispatch
+ *
+ * (c) 2015 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/macros.h>
+
+#include "dispatch.h"
+
+/*----- Intel x86/AMD64 feature probing -----------------------------------*/
+
+#if CPUFAM_X86 || CPUFAM_AMD64
+
+#  define EFLAGS_ID (1u << 21)
+#  define CPUID1D_SSE2 (1u << 26)
+#  define CPUID1D_FXSR (1u << 24)
+#  define CPUID1C_AESNI (1u << 25)
+#  define CPUID1C_RDRAND (1u << 30)
+
+struct cpuid { unsigned a, b, c, d; };
+
+/* --- @cpuid@ --- *
+ *
+ * Arguments:  @struct cpuid *cc@ = where to write the result
+ *             @unsigned a, c@ = EAX and ECX registers to set
+ *
+ * Returns:    ---
+ *
+ * Use:                Minimal C wrapper around the x86 `CPUID' instruction.  Checks
+ *             that the instruction is actually available before invoking
+ *             it; fills the output structure with zero if it's not going to
+ *             work.
+ */
+
+#ifdef __GNUC__
+#  if CPUFAM_X86
+static __inline__ unsigned getflags(void)
+  { unsigned f; __asm__ ("pushf; popl %0" : "=g" (f)); return (f); }
+static __inline__ unsigned setflags(unsigned f)
+{
+  unsigned ff;
+  __asm__ ("pushf; pushl %1; popf; pushf; popl %0; popf"
+          : "=g" (ff)
+          : "g" (f));
+  return (ff);
+}
+#  else
+static __inline__ unsigned long getflags(void)
+  { unsigned long f; __asm__ ("pushf; popq %0" : "=g" (f)); return (f); }
+static __inline__ unsigned long long setflags(unsigned long f)
+{
+  unsigned long ff;
+  __asm__ ("pushf; pushq %1; popf; pushf; popq %0; popf"
+          : "=g" (ff)
+          : "g" (f));
+  return (ff);
+}
+#  endif
+#endif
+
+static void cpuid(struct cpuid *cc, unsigned a, unsigned c)
+{
+#ifdef __GNUC__
+  unsigned f;
+#endif
+
+  cc->a = cc->b = cc->c = cc->d = 0;
+
+#ifdef __GNUC__
+  /* Stupid dance to detect whether the CPUID instruction is available. */
+  f = getflags();
+  if (!(setflags(f |  EFLAGS_ID) & EFLAGS_ID) ||
+       setflags(f & ~EFLAGS_ID) & EFLAGS_ID) {
+    dispatch_debug("CPUID instruction not available");
+    return;
+  }
+  setflags(f);
+
+  /* Alas, EBX is magical in PIC code, so abuse ESI instead.  This isn't
+   * pretty, but it works.
+   */
+#  if CPUFAM_X86
+  __asm__ ("pushl %%ebx; cpuid; movl %%ebx, %%esi; popl %%ebx"
+          : "=a" (cc->a), "=S" (cc->b), "=c" (cc->c), "=d" (cc->d)
+          : "a" (a) , "c" (c));
+#  elif CPUFAM_AMD64
+  __asm__ ("pushq %%rbx; cpuid; movl %%ebx, %%esi; popq %%rbx"
+          : "=a" (cc->a), "=S" (cc->b), "=c" (cc->c), "=d" (cc->d)
+          : "a" (a) , "c" (c));
+#  else
+#    error "I'm confused."
+#  endif
+  dispatch_debug("CPUID(%08x, %08x) -> %08x, %08x, %08x, %08x",
+                a, c, cc->a, cc->b, cc->c, cc->d);
+#else
+  dispatch_debug("GNU inline assembler not available; can't CPUID");
+#endif
+}
+
+static unsigned cpuid_maxleaf(void)
+  { struct cpuid c; cpuid(&c, 0, 0); return (c.a); }
+
+/* --- @cpuid_features_p@ --- *
+ *
+ * Arguments:  @unsigned dbits@ = bits to check in EDX
+ *             @unsigned cbits@ = bits to check in ECX
+ *
+ * Returns:    Nonzero if all the requested bits are set in the CPUID result
+ *             on leaf 1.
+ */
+
+static int cpuid_features_p(unsigned dbits, unsigned cbits)
+{
+  struct cpuid c;
+  if (cpuid_maxleaf() < 1) return (0);
+  cpuid(&c, 1, 0);
+  return ((c.d & dbits) == dbits && (c.c & cbits) == cbits);
+}
+
+/* --- @xmm_registers_available_p@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Nonzero if the operating system has made the XMM registers
+ *             available for use.
+ */
+
+static int xmm_registers_available_p(void)
+{
+#ifdef __GNUC__
+  unsigned f;
+  /* This hack is by Agner Fog.  Use FXSAVE/FXRSTOR to figure out whether the
+   * XMM registers are actually alive.
+   */
+  if (!cpuid_features_p(CPUID1D_FXSR, 0)) return (0);
+#  if CPUFAM_X86
+  __asm__ ("movl %%esp, %%edx; subl $512, %%esp; andl $~15, %%esp\n"
+          "fxsave (%%esp)\n"
+          "movl 160(%%esp), %%eax; xorl $0xaaaa5555, 160(%%esp)\n"
+          "fxrstor (%%esp); fxsave (%%esp)\n"
+          "movl 160(%%esp), %%ecx; movl %%eax, 160(%%esp)\n"
+          "fxrstor (%%esp); movl %%edx, %%esp\n"
+          "xorl %%ecx, %%eax"
+          : "=a" (f)
+          : /* no inputs */
+          : "%ecx", "%edx");
+#  elif CPUFAM_AMD64
+  __asm__ ("movq %%rsp, %%rdx; subq $512, %%rsp; andq $~15, %%rsp\n"
+          "fxsave (%%rsp)\n"
+          "movl 160(%%rsp), %%eax; xorl $0xaaaa5555, 160(%%rsp)\n"
+          "fxrstor (%%rsp); fxsave (%%rsp)\n"
+          "movl 160(%%rsp), %%ecx; movl %%eax, 160(%%rsp)\n"
+          "fxrstor (%%rsp); movq %%rdx, %%rsp\n"
+          "xorl %%ecx, %%eax"
+          : "=a" (f)
+          : /* no inputs */
+          : "%ecx", "%rdx");
+#  else
+#    error "I'm confused."
+#  endif
+  dispatch_debug("XMM registers %savailable", f ? "" : "not ");
+  return (f);
+#else
+  dispatch_debug("GNU inline assembler not available; can't check for XMM");
+  return (0);
+#endif
+}
+
+#endif
+
+/*----- General feature probing using auxiliary vectors -------------------*/
+
+/* Try to find the system's definitions for auxiliary vector entries. */
+#ifdef HAVE_SYS_AUXV_H
+#  include <sys/auxv.h>
+#else
+#  ifdef HAVE_LINUX_AUXVEC_H
+#    include <linux/auxvec.h>
+#  endif
+#  ifdef HAVE_ASM_HWCAP_H
+#    include <asm/hwcap.h>
+#  endif
+#endif
+
+/* The type of entries in the auxiliary vector.  I'm assuming that `unsigned
+ * long' matches each platform's word length; if this is false then we'll
+ * need some host-specific tweaking here.
+ */
+union auxval { long i; unsigned long u; const void *p; };
+struct auxentry { unsigned long type; union auxval value; };
+
+/* Register each CPU family's interest in the auxiliary vector.  Make sure
+ * that the necessary entry types are defined.  This is primarily ordered by
+ * entry type to minimize duplication.
+ */
+#if defined(AT_HWCAP) && CPUFAM_ARMEL
+#  define WANT_ANY 1
+#  define WANT_AT_HWCAP(_) _(AT_HWCAP, u, hwcap)
+#endif
+
+/* If we couldn't find any interesting entries then we can switch all of this
+ * machinery off.  Also do that if we have no means for atomic updates.
+ */
+#if WANT_ANY && CPU_DISPATCH_P
+
+/* The main output of this section is a bitmask of detected features.  The
+ * least significant bit will be set if we've tried to probe.  Always access
+ * this using `DISPATCH_LOAD' and `DISPATCH_STORE'.
+ */
+static unsigned hwcaps = 0;
+
+/* For each potentially interesting type which turned out not to exist or be
+ * wanted, define a dummy macro for the sake of the next step.
+ */
+#ifndef WANT_AT_HWCAP
+#  define WANT_AT_HWCAP(_)
+#endif
+
+/* For each CPU family, define two lists.
+ *
+ *   * `WANTAUX' is a list of the `WANT_AT_MUMBLE' macros which the CPU
+ *     family tried to register interest in above.  Each entry contains the
+ *     interesting auxiliary vector entry type, the name of the union branch
+ *     for its value, and the name of the slot in `struct auxprobe' in which
+ *     to store the value.
+ *
+ *   * `CAPMAP' is a list describing the output features which the CPU family
+ *     intends to satisfy from the auxiliary vector.  Each entry contains a
+ *     feature name suffix, and the token name (for `check_env').
+ */
+#if CPUFAM_ARMEL
+#  define WANTAUX(_)                                                   \
+       WANT_AT_HWCAP(_)
+#  define CAPMAP(_)                                                    \
+       _(ARM_VFP, "arm:vfp")                                           \
+       _(ARM_NEON, "arm:neon")                                         \
+       _(ARM_V4, "arm:v4")                                             \
+       _(ARM_D32, "arm:d32")
+#endif
+
+/* Build the bitmask for `hwcaps' from the `CAPMAP' list. */
+enum {
+  HFI_PROBED = 0,
+#define HFI__ENUM(feat, tok) HFI_##feat,
+  CAPMAP(HFI__ENUM)
+#undef HFI__ENUM
+  HFI__END
+};
+enum {
+  HF_PROBED = 1,
+#define HF__FLAG(feat, tok) HF_##feat = 1 << HFI_##feat,
+  CAPMAP(HF__FLAG)
+#undef HF__FLAG
+  HF__END
+};
+
+/* Build a structure in which we can capture the interesting data from the
+ * auxiliary vector.
+ */
+#define AUXUTYPE_i long
+#define AUXUTYPE_u unsigned long
+#define AUXUTYPE_p const void *
+struct auxprobe {
+#define AUXPROBE__SLOT(type, ubranch, slot) AUXUTYPE_##ubranch slot;
+  WANTAUX(AUXPROBE__SLOT)
+#undef AUXPROBE_SLOT
+};
+
+/* --- @probe_hwcaps@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Attempt to find the auxiliary vector (which is well hidden)
+ *             and discover interesting features from it.
+ */
+
+static void probe_hwcaps(void)
+{
+  unsigned hw = HF_PROBED;
+  struct auxprobe probed = { 0 };
+
+  /* Populate `probed' with the information we manage to retrieve from the
+   * auxiliary vector.  Slots we couldn't find are left zero-valued.
+   */
+#if defined(HAVE_GETAUXVAL)
+  /* Shiny new libc lets us request individual entry types.  This is almost
+   * too easy.
+   */
+#  define CAP__GET(type, slot, ubranch)                                        \
+       probed.slot.ubranch = (AUXUTYPE_##ubranch)getauxval(type);
+  WANTAUX(CAP__GET)
+#else
+  /* Otherwise we're a bit stuck, really.  Modern Linux kernels make a copy
+   * of the vector available in `/procc' so we could try that.
+   *
+   * The usual place is stuck on the end of the environment vector, but that
+   * may well have moved, and we have no way of telling whether it has or
+   * whether there was ever an auxiliary vector there at all; so don't do
+   * that.
+   */
+  {
+    FILE *fp = 0;
+    unsigned char *p = 0, *q = 0;
+    const struct auxentry *a;
+    size_t sz, off, n;
+
+    /* Open the file and read it into a memory chunk. */
+    if ((fp = fopen("/proc/self/auxv", "rb")) == 0) goto clean;
+    sz = 4096; off = 0;
+    if ((p = malloc(sz)) == 0) goto clean;
+    for (;;) {
+      n = fread(p + off, 1, sz - off, fp);
+      off += n;
+      if (off < sz) break;
+      sz *= 2; if ((q = realloc(p, sz)) == 0) break;
+      p = q;
+    }
+
+    /* Work through the vector (or as much of it as we found) and extract the
+     * types we're interested in.
+     */
+    for (a = (const struct auxentry *)p,
+          n = sz/sizeof(struct auxentry);
+        n--; a++) {
+      switch (a->type) {
+#define CAP__SWITCH(type, ubranch, slot)                               \
+       case type: probed.slot = a->value.ubranch; break;
+       WANTAUX(CAP__SWITCH)
+       case AT_NULL: goto clean;
+      }
+    }
+
+  clean:
+    if (p) free(p);
+    if (fp) fclose(fp);
+  }
+#endif
+
+  /* Each CPU family now has to pick through what was found and stashed in
+   * `probed', and set the appropriate flag bits in `hw'.
+   */
+#if CPUFAM_ARMEL
+  if (probed.hwcap & HWCAP_VFPv3) hw |= HF_ARM_VFP;
+  if (probed.hwcap & HWCAP_NEON) hw |= HF_ARM_NEON;
+  if (probed.hwcap & HWCAP_VFPD32) hw |= HF_ARM_D32;
+  if (probed.hwcap & HWCAP_VFPv4) hw |= HF_ARM_V4;
+#endif
+
+  /* Store the bitmask of features we probed for everyone to see. */
+  DISPATCH_STORE(hwcaps, hw);
+
+  /* Finally, make a report about the things we found.  (Doing this earlier
+   * will pointlessly widen the window in which multiple threads will do the
+   * above auxiliary-vector probing.)
+   */
+#define CAP__DEBUG(feat, tok)                                          \
+  dispatch_debug("check auxv for feature `%s': %s", tok,               \
+                hw & HF_##feat ? "available" : "absent");
+  CAPMAP(CAP__DEBUG)
+#undef CAP__DEBUG
+}
+
+/* --- @get_hwcaps@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    A mask of hardware capabilities and other features, as probed
+ *             from the auxiliary vector.
+ */
+
+static unsigned get_hwcaps(void)
+{
+  unsigned hw;
+
+  DISPATCH_LOAD(hwcaps, hw);
+  if (!(hwcaps & HF_PROBED)) { probe_hwcaps(); DISPATCH_LOAD(hwcaps, hw); }
+  return (hw);
+}
+
+#endif
+
+/*----- External interface ------------------------------------------------*/
+
+/* --- @dispatch_debug@ --- *
+ *
+ * Arguments:  @const char *fmt@ = a format string
+ *             @...@ = additional arguments
+ *
+ * Returns:    ---
+ *
+ * Use:                Writes a formatted message to standard output if dispatch
+ *             debugging is enabled.
+ */
+
+void dispatch_debug(const char *fmt, ...)
+{
+  va_list ap;
+  const char *e = getenv("CATACOMB_CPUDISPATCH_DEBUG");
+
+  if (e && *e != 'n' && *e != '0') {
+    va_start(ap, fmt);
+    fputs("Catacomb CPUDISPATCH: ", stderr);
+    vfprintf(stderr, fmt, ap);
+    fputc('\n', stderr);
+    va_end(ap);
+  }
+}
+
+/* --- @check_env@ --- *
+ *
+ * Arguments:  @const char *ftok@ = feature token
+ *
+ * Returns:    Zero if the feature is forced off; positive if it's forced
+ *             on; negative if the user hasn't decided.
+ *
+ * Use:                Checks the environment variable `CATACOMB_CPUFEAT' for the
+ *             feature token @ftok@.  The variable, if it exists, should be
+ *             a space-separated sequence of `+tok' and `-tok' items.  These
+ *             tokens may end in `*', which matches any suffix.
+ */
+
+static int IGNORABLE check_env(const char *ftok)
+{
+  const char *p, *q, *pp;
+  int d;
+
+  p = getenv("CATACOMB_CPUFEAT");
+  if (!p) return (-1);
+
+  for (;;) {
+    while (isspace((unsigned char)*p)) p++;
+    if (!*p) return (-1);
+    switch (*p) {
+      case '+': d = +1; p++; break;
+      case '-': d =  0; p++; break;
+      default:  d = -1;      break;
+    }
+    for (q = p; *q && !isspace((unsigned char)*q); q++);
+    if (d >= 0) {
+      for (pp = ftok; p < q && *pp && *p == *pp; p++, pp++);
+      if ((p == q && !*pp) || (*p == '*' && p + 1 == q)) return (d);
+    }
+    p = q;
+  }
+  return (-1);
+}
+
+/* --- @cpu_feature_p@ --- *
+ *
+ * Arguments:  @unsigned feat@ = a @CPUFEAT_...@ code
+ *
+ * Returns:    Nonzero if the feature is available.
+ */
+
+#include <stdio.h>
+
+static int IGNORABLE
+  feat_debug(const char *ftok, const char *check, int verdict)
+{
+  if (verdict >= 0) {
+    dispatch_debug("feature `%s': %s -> %s", ftok, check,
+                  verdict ? "available" : "absent");
+  }
+  return (verdict);
+}
+
+int cpu_feature_p(int feat)
+{
+  int IGNORABLE f;
+  IGNORE(f);
+#define CASE_CPUFEAT(feat, ftok, cond) case CPUFEAT_##feat:            \
+  if ((f = feat_debug(ftok, "environment override",                    \
+                     check_env(ftok))) >= 0)                           \
+    return (f);                                                                \
+  else                                                                 \
+    return (feat_debug(ftok, "runtime probe", cond));
+
+  switch (feat) {
+#if CPUFAM_X86 || CPUFAM_AMD64
+    CASE_CPUFEAT(X86_SSE2, "x86:sse2",
+                xmm_registers_available_p() &&
+                cpuid_features_p(CPUID1D_SSE2, 0));
+    CASE_CPUFEAT(X86_AESNI, "x86:aesni",
+                xmm_registers_available_p() &&
+                cpuid_features_p(CPUID1D_SSE2, CPUID1C_AESNI));
+    CASE_CPUFEAT(X86_RDRAND, "x86:rdrand",
+                cpuid_features_p(0, CPUID1C_RDRAND));
+#endif
+#ifdef CAPMAP
+#  define FEATP__CASE(feat, tok)                                       \
+       CASE_CPUFEAT(feat, tok, get_hwcaps() & HF_##feat)
+    CAPMAP(FEATP__CASE)
+#undef FEATP__CASE
+#endif
+    default:
+      dispatch_debug("denying unknown feature %d", feat);
+      return (0);
+  }
+#undef CASE_CPUFEAT
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/base/dispatch.h b/base/dispatch.h
new file mode 100644 (file)
index 0000000..1983a5a
--- /dev/null
@@ -0,0 +1,194 @@
+/* -*-c-*-
+ *
+ * CPU-specific dispatch
+ *
+ * (c) 2015 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.
+ */
+
+#ifndef CATACOMB_DISPATCH_H
+#define CATACOMB_DISPATCH_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/macros.h>
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- Atomic data access machinery --- *
+ *
+ * If they're available, use GCC's `__atomic_*' intrinsics.  If that doesn't
+ * work and we're using one of a small number of processors I'm sure won't
+ * mind, then just stick with simple memory access.  Otherwise turn
+ * dispatching off, because it probably isn't thread-safe.
+ */
+
+#if GCC_VERSION_P(4, 7)
+#  define CPU_DISPATCH_P 1
+#  define DISPATCH_LOAD(g, v)                                          \
+       ((v) = __atomic_load_n(&(g), __ATOMIC_RELAXED))
+#  define DISPATCH_STORE(g, v)                                         \
+       (__atomic_store_n(&(g), (v), __ATOMIC_RELAXED))
+#elif defined(__i386__) || defined(__amd64__) ||                       \
+      defined(__arm__) || defined(__aarch64__) ||                      \
+      defined(__mips__)
+#  define CPU_DISPATCH_P 1
+#  define DISPATCH_LOAD(g, v) ((v) = (g))
+#  define DISPATCH_STORE(g, v) ((g) = (v))
+#endif
+
+/* --- A simple hack --- */
+
+#ifndef EMPTY
+#  define EMPTY
+#endif
+
+/* --- @CPU_DISPATCH@ --- *
+ *
+ * Arguments:  @stcls@ = storage class for the main @ext@ function
+ *                     (typically either @static@ or @EMPTY@)
+ *             @rtn@ = prefix for tail-calling a function of the appropriate
+ *                     type (either @(void)@ or @return@)
+ *             @ret@ = return type for the function
+ *             @ext@ = name for the main function (other named are derived
+ *                     from this)
+ *             @argdecls@ = parenthesis-enclosed list of argument types
+ *             @args@ = parenthesis-enclosed list of argument names only
+ *             @pick@ = function to select appropriate implementation
+ *             @dflt@ = fallback implementation
+ *
+ * Use:                Main machinery for CPU-specfic dispatching.
+ *
+ *             The macro defines a function
+ *
+ *                     @stcls ret ext argdcls@
+ *
+ *             The first time @ext@ is called, it will invoke @pick@ to
+ *             select and a return a pointer to an appropriate
+ *             implementation for the runtime environment.  Subsequent calls
+ *             to @ext@ will (usually) call this preferred implementation
+ *             directly.
+ *
+ *             Some target platforms may not be able to establish the
+ *             necessary function pointer in a threadsafe way.  On such
+ *             platforms, the dispatch machinery is disabled and @ext@ will
+ *             simply call @dflt@.
+ *
+ *             Some additional declarations are made.  As a convenience,
+ *             @ext__functype@ is the function type of @ext@.  Declarations
+ *             are made for @pick@ and @dflt@, as @static@ functions.
+ */
+
+#ifdef CPU_DISPATCH_P
+
+#define CPU_DISPATCH(stcls, rtn, ret, ext, argdecls, args, pick, dflt) \
+                                                                       \
+typedef ret ext##__functype argdecls;                                  \
+static ret dflt argdecls;                                              \
+static ret ext##__dispatch argdecls;                                   \
+static ext##__functype *pick(void);                                    \
+static ext##__functype *ext##__ptr = ext##__dispatch;                  \
+                                                                       \
+static ret ext##__dispatch argdecls                                    \
+{                                                                      \
+  ext##__functype *f = pick();                                         \
+  DISPATCH_STORE(ext##__ptr, f);                                       \
+  rtn f args;                                                          \
+}                                                                      \
+                                                                       \
+stcls ret ext argdecls                                                 \
+{                                                                      \
+  ext##__functype *f;                                                  \
+  DISPATCH_LOAD(ext##__ptr, f);                                                \
+  rtn f args;                                                          \
+}
+
+#else
+
+#define CPU_DISPATCH(stcls, rtn, ret, ext, argdecls, args, pick, dflt) \
+                                                                       \
+typedef ret ext##__functype argdecls;                                  \
+static ret dflt argdecls;                                              \
+static ext##__functype *pick(void) IGNORABLE;                          \
+                                                                       \
+stcls ret ext argdecls { rtn dflt args; }
+
+#endif
+
+/* --- Some macros for producing useful debugging --- */
+
+#define DISPATCH_PICK_COND(what, func, cond) do {                      \
+  if (cond) {                                                          \
+    dispatch_debug("picked `%s' for `%s'", #func, #what);              \
+    return (func);                                                     \
+  }                                                                    \
+} while (0)
+#define DISPATCH_PICK_FALLBACK(what, func) do {                                \
+  dispatch_debug("using default `%s'", #what);                         \
+  return (func);                                                       \
+} while (0)
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @dispatch_debug@ --- *
+ *
+ * Arguments:  @const char *fmt@ = a format string
+ *             @...@ = additional arguments
+ *
+ * Returns:    ---
+ *
+ * Use:                Writes a formatted message to standard output if dispatch
+ *             debugging is enabled.
+ */
+
+extern void dispatch_debug(const char */*fmt*/, ...);
+
+/* --- @cpu_feature_p@ --- *
+ *
+ * Arguments:  @unsigned feat@ = a @CPUFEAT_...@ code
+ *
+ * Returns:    Nonzero if the feature is available.
+ */
+
+enum {
+  CPUFEAT_X86_SSE2,                    /* Streaming SIMD Extensions 2 */
+  CPUFEAT_X86_AESNI,                   /* AES Native Instructions */
+  CPUFEAT_ARM_VFP,                     /* VFP floating-point (v3 or v4) */
+  CPUFEAT_ARM_NEON,                    /* Advanced SIMD (v1 or v2) */
+  CPUFEAT_ARM_V4,                      /* VFPv4 and/or SIMD v2 */
+  CPUFEAT_ARM_D32,                     /* 32 double registers, not 16 */
+  CPUFEAT_X86_RDRAND                   /* Built-in entropy source */
+};
+
+extern int cpu_feature_p(int /*feat*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
index ded8b11..2bbf56f 100755 (executable)
@@ -1,4 +1,4 @@
 #! /bin/sh -ex
 for gen in symm/modes.am symm/stubs.am; do
-  if [ ! -r $gen ]; then touch -t197001010000.00 $gen; fi
+  if [ ! -r $gen ]; then touch -t197101010000.00 $gen; fi
 done
index 8496cc3..3c8cb37 100644 (file)
@@ -31,7 +31,8 @@ mdw_AUTO_VERSION
 AC_INIT([catacomb], AUTO_VERSION, [mdw@distorted.org.uk])
 AC_CONFIG_SRCDIR([catacomb.pc.in])
 AC_CONFIG_AUX_DIR([config])
-AM_INIT_AUTOMAKE([foreign parallel-tests])
+AM_INIT_AUTOMAKE([foreign parallel-tests color-tests subdir-objects])
+AC_CANONICAL_HOST
 mdw_SILENT_RULES
 
 AC_PROG_CC
@@ -39,13 +40,107 @@ AX_CFLAGS_WARN_ALL
 AM_PROG_LIBTOOL
 mdw_LIBTOOL_VERSION_INFO
 
+AM_PROG_AS
+
 AC_PROG_YACC
 
 AC_SUBST(AM_CFLAGS)
 
 dnl--------------------------------------------------------------------------
+dnl Host-specific configuration.
+
+AC_MSG_CHECKING([CPU family and ABI])
+
+dnl The table of CPU families and ABIs which we might support.  Support is
+dnl not uniform: each dispatched function might or might not have an
+dnl implementation for any particular CPU/ABI combination.
+AC_DEFUN([catacomb_CPU_FAMILIES],
+  [$1([i[[3-6]]86,cygwin], [x86], [win])
+   $1([i[[3-6]]86,*], [x86], [sysv])
+   $1([x86_64,cygwin], [amd64], [win])
+   $1([x86_64,*], [amd64], [sysv])
+   $1([arm,*-gnueabi | arm,*-gnueabihf | dnl
+       armv*,*-gnueabi | armv*,*-gnueabihf], [armel], [gnueabi])])
+
+dnl A utility to clear the `seen' flags, used so as to process each CPU or
+dnl ABI once.
+m4_define([catacomb_CLEAR_FLAGS],
+[m4_ifdef([catacomb_seen_cpu/$2],
+         [m4_undefine([catacomb_seen_cpu/$2])])dnl
+m4_ifdef([catacomb_seen_abi/$3],
+         [m4_undefine([catacomb_seen_abi/$3])])])
+
+dnl Identify the current host.
+case $host_cpu,$host_os in
+  m4_define([catacomb_CPU_CASE],
+    [$1) CPUFAM=$2 ABI=$3 ;;
+])
+  catacomb_CPU_FAMILIES([catacomb_CPU_CASE])
+  *) CPUFAM=nil ABI=nil ;;
+esac
+
+dnl Figure out the current CPU.
+catacomb_CPU_FAMILIES([catacomb_CLEAR_FLAGS])
+case $CPUFAM in
+  m4_define([catacomb_DEFINE_CPU],
+    [m4_ifdef([catacomb_seen_cpu/$2], [],
+      [$2)
+       AC_DEFINE([CPUFAM_]m4_translit([$2], [a-z], [A-Z]), [1],
+                 [Define if host CPU family is \`$2\'.])
+       ;;m4_define([catacomb_seen_cpu/$2], [t])])])
+  catacomb_CPU_FAMILIES([catacomb_DEFINE_CPU])
+  nil) ;;
+  *) AC_MSG_ERROR([BUG: unexpected cpufam \`$CPUFAM']) ;;
+esac
+AC_SUBST([CPUFAM])
+
+dnl Figure out the current ABI.
+catacomb_CPU_FAMILIES([catacomb_CLEAR_FLAGS])
+case $ABI in
+  m4_define([catacomb_DEFINE_ABI],
+    [m4_ifdef([catacomb_seen_abi/$3], [],
+      [$3)
+       AC_DEFINE([ABI_]m4_translit([$3], [a-z], [A-Z]), [1],
+                 [Define if host ABI variant is \`$3\'.])
+       ;;m4_define([catacomb_seen_abi/$3], [t])])])
+  catacomb_CPU_FAMILIES([catacomb_DEFINE_ABI])
+  nil) ;;
+  *) AC_MSG_ERROR([BUG: unexpected ABI \`$ABI']) ;;
+esac
+AC_SUBST([ABI])
+
+dnl Establish Automake conditions for things.
+catacomb_CPU_FAMILIES([catacomb_CLEAR_FLAGS])
+m4_define([catacomb_COND_CPU],
+[m4_define([_CPU], m4_translit([$2], [a-z], [A-Z]))
+m4_define([_ABI], m4_translit([$3], [a-z], [A-Z]))
+AM_CONDITIONAL([CPUABI_]_CPU[_]_ABI, [test x$CPUFAM/$ABI = x$2/$3])
+m4_ifdef([catacomb_seen_cpu/$2], [],
+[AM_CONDITIONAL([CPUFAM_]_CPU, [test x$CPUFAM = x$2])dnl
+m4_define([catacomb_seen_cpu/$2], [t])])
+m4_ifdef([catacomb_seen_abi/$3], [],
+[AM_CONDITIONAL([ABI_]_ABI, [test x$ABI = x$3])dnl
+m4_define([catacomb_seen_abi/$3], [t])])])
+catacomb_CPU_FAMILIES([catacomb_COND_CPU])
+AM_CONDITIONAL([KNOWN_CPUFAM], [test x$CPUFAM != xnil])
+
+dnl Report on what we found.
+case $CPUFAM in
+  nil) AC_MSG_RESULT([not supported]) ;;
+  *) AC_MSG_RESULT([$CPUFAM/$ABI]) ;;
+esac
+
+dnl Some equipment wanted for checking CPU features at runtime.
+AC_CHECK_HEADERS([asm/hwcap.h])
+AC_CHECK_HEADERS([sys/auxv.h])
+AC_CHECK_HEADERS([linux/auxvec.h])
+AC_CHECK_FUNCS([getauxval])
+
+dnl--------------------------------------------------------------------------
 dnl C programming environment.
 
+CATACOMB_LIBS=
+
 dnl Find out if we're cross-compiling.
 AM_CONDITIONAL([CROSS_COMPILING], [test "$cross_compiling" = yes])
 
@@ -128,6 +223,15 @@ AC_SUBST([limits])
 
 dnl Functions used for noise-gathering.
 AC_CHECK_FUNCS([setgroups])
+AC_CHECK_HEADERS([linux/random.h])
+mdw_ORIG_LIBS=$LIBS LIBS=$CATACOMB_LIBS
+AC_SEARCH_LIBS([clock_gettime], [rt])
+CATACOMB_LIBS=$LIBS LIBS=$mdw_ORIG_LIBS
+if test $ac_cv_search_clock_gettime != no; then
+  AC_DEFINE([HAVE_CLOCK_GETTIME], [1],
+           [Define if you have the \`clock_gettime' function.])
+fi
+AC_CHECK_FUNCS([getentropy])
 AC_CACHE_CHECK([whether the freewheel noise generator will work],
        [catacomb_cv_freewheel],
 [AC_TRY_LINK(
@@ -155,6 +259,9 @@ LIBS=$mdw_ORIG_LIBS
 dnl Memory locking support.
 AC_CHECK_FUNCS([mlock])
 
+dnl Set the master libraries we need.
+AC_SUBST([CATACOMB_LIBS])
+
 dnl Necessary support libraries.
 PKG_CHECK_MODULES([mLib], [mLib >= 2.2.2.1])
 AM_CFLAGS="$AM_CFLAGS $mLib_CFLAGS"
index 2890f4c..d84c92d 100644 (file)
@@ -1,3 +1,13 @@
+catacomb (2.2.2) experimental; urgency=low
+
+  * build: Cope with newer Autotools and related equipment.
+  * Miscellaneous small fixes for Cygwin.
+  * catacomb2 (mp_testbit): Fix overread on reading one-bit-past-the-end;
+    particularly, this causes a segfault reading bit zero of a zero-length
+    integer.
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Sat, 04 Jun 2016 01:12:01 +0100
+
 catacomb (2.2.1.1) experimental; urgency=low
 
   * Arrange that catacomb-dev Depends on correct version of mlib-dev.  It
index cec98bb..e64e6b4 100755 (executable)
@@ -1,2 +1,6 @@
 #! /usr/bin/make -f
 %:; dh $@ --parallel -Bdebian/build
+
+override_dh_auto_test:
+       dh_auto_test --parallel -Bdebian/build
+       CATACOMB_CPUFEAT="-*" dh_auto_test --parallel -Bdebian/build
index 7cabf89..908d268 100644 (file)
@@ -84,29 +84,29 @@ libmath_la_SOURCES  += exp.c
 ## Main user-visible multiprecision arithmetic.
 pkginclude_HEADERS     += mp.h
 libmath_la_SOURCES     += mp-arith.c
-TESTS                  += mp-arith.$t
+TESTS                  += mp-arith.t$(EXEEXT)
 libmath_la_SOURCES     += mp-const.c
 libmath_la_SOURCES     += mp-exp.c mp-exp.h
 libmath_la_SOURCES     += mp-gcd.c
-TESTS                  += mp-gcd.$t
+TESTS                  += mp-gcd.t$(EXEEXT)
 libmath_la_SOURCES     += mp-io.c
 libmath_la_SOURCES     += mp-jacobi.c
-TESTS                  += mp-jacobi.$t
+TESTS                  += mp-jacobi.t$(EXEEXT)
 libmath_la_SOURCES     += mp-mem.c
 libmath_la_SOURCES     += mp-misc.c
 libmath_la_SOURCES     += mp-modexp.c
-TESTS                  += mp-modexp.$t
+TESTS                  += mp-modexp.t$(EXEEXT)
 libmath_la_SOURCES     += mp-modsqrt.c
-TESTS                  += mp-modsqrt.$t
+TESTS                  += mp-modsqrt.t$(EXEEXT)
 libmath_la_SOURCES     += mp-sqrt.c
-TESTS                  += mp-sqrt.$t
+TESTS                  += mp-sqrt.t$(EXEEXT)
 libmath_la_SOURCES     += mp-test.c
 EXTRA_DIST             += t/mp
 
 ## Computing Fibonacci numbers.
 pkginclude_HEADERS     += mp-fibonacci.h
 libmath_la_SOURCES     += mp-fibonacci.c
-TESTS                  += mp-fibonacci.$t
+TESTS                  += mp-fibonacci.t$(EXEEXT)
 
 ## Special memory allocation for multiprecision integers.
 pkginclude_HEADERS     += mparena.h
@@ -115,36 +115,36 @@ libmath_la_SOURCES        += mparena.c
 ## Barrett reduction, an efficient method for modular reduction.
 pkginclude_HEADERS     += mpbarrett.h
 libmath_la_SOURCES     += mpbarrett.c
-TESTS                  += mpbarrett.$t
+TESTS                  += mpbarrett.t$(EXEEXT)
 libmath_la_SOURCES     += mpbarrett-exp.c mpbarrett-mexp.c mpbarrett-exp.h
-TESTS                  += mpbarrett-exp.$t mpbarrett-mexp.$t
-TESTS                  += mpbarrett.$t
+TESTS                  += mpbarrett-exp.t$(EXEEXT) mpbarrett-mexp.t$(EXEEXT)
+TESTS                  += mpbarrett.t$(EXEEXT)
 EXTRA_DIST             += t/mpbarrett
 
 ## Solving congruences using the Chinese Remainder Theorem.
 pkginclude_HEADERS     += mpcrt.h
 libmath_la_SOURCES     += mpcrt.c
-TESTS                  += mpcrt.$t
+TESTS                  += mpcrt.t$(EXEEXT)
 EXTRA_DIST             += t/mpcrt
 
 ## Conversions between machine-native and multiprecision integers.
 pkginclude_HEADERS     += mpint.h
 libmath_la_SOURCES     += mpint.c
-TESTS                  += mpint.$t
+TESTS                  += mpint.t$(EXEEXT)
 EXTRA_DIST             += t/mpint
 
 ## Montgomery reduction, a clever method for modular arithmetic.
 pkginclude_HEADERS     += mpmont.h
 libmath_la_SOURCES     += mpmont.c
-TESTS                  += mpmont.$t
+TESTS                  += mpmont.t$(EXEEXT)
 libmath_la_SOURCES     += mpmont-exp.c mpmont-mexp.c mpmont-exp.h
-TESTS                  += mpmont-exp.$t mpmont-mexp.$t
+TESTS                  += mpmont-exp.t$(EXEEXT) mpmont-mexp.t$(EXEEXT)
 EXTRA_DIST             += t/mpmont
 
 ## Efficient multiplication of many small numbers.
 pkginclude_HEADERS     += mpmul.h
 libmath_la_SOURCES     += mpmul.c
-TESTS                  += mpmul.$t
+TESTS                  += mpmul.t$(EXEEXT)
 
 ## Generating random numbers.
 pkginclude_HEADERS     += mprand.h
@@ -154,7 +154,7 @@ libmath_la_SOURCES  += mprand.c
 ## representations.
 pkginclude_HEADERS     += mpreduce.h
 libmath_la_SOURCES     += mpreduce.c mpreduce-exp.h
-TESTS                  += mpreduce.$t
+TESTS                  += mpreduce.t$(EXEEXT)
 EXTRA_DIST             += t/mpreduce
 
 ## Iteratiion over the bianry representation of multiprecision integers.
@@ -165,7 +165,7 @@ libmath_la_SOURCES  += mpscan.c
 ## representations.
 pkginclude_HEADERS     += mptext.h
 libmath_la_SOURCES     += mptext.c
-TESTS                  += mptext.$t
+TESTS                  += mptext.t$(EXEEXT)
 libmath_la_SOURCES     += mptext-dstr.c
 libmath_la_SOURCES     += mptext-file.c
 libmath_la_SOURCES     += mptext-len.c
@@ -175,9 +175,9 @@ EXTRA_DIST          += t/mptext
 ## Low-level multiprecision arithmetic.
 pkginclude_HEADERS     += mpx.h bitops.h mpw.h
 libmath_la_SOURCES     += mpx.c
-TESTS                  += mpx.$t
+TESTS                  += mpx.t$(EXEEXT)
 libmath_la_SOURCES     += karatsuba.h mpx-kmul.c mpx-ksqr.c
-TESTS                  += mpx-kmul.$t mpx-ksqr.$t
+TESTS                  += mpx-kmul.t$(EXEEXT) mpx-ksqr.t$(EXEEXT)
 noinst_PROGRAMS                += bittest
 TESTS                  += bittest
 EXTRA_DIST             += t/mpx
@@ -190,7 +190,7 @@ libmath_la_SOURCES  += qdparse.c
 ## Pollard's `rho' algorithm for determining discrete logarithms.
 pkginclude_HEADERS     += rho.h
 libmath_la_SOURCES     += rho.c
-TESTS                  += rho.$t
+TESTS                  += rho.t$(EXEEXT)
 
 ###--------------------------------------------------------------------------
 ### Prime number checking, searching, and related jobs.
@@ -201,18 +201,19 @@ pkginclude_HEADERS        += limlee.h
 libmath_la_SOURCES     += limlee.c
 
 ## A table of small prime numbers.
-pkginclude_HEADERS     += $(precomp)/primetab.h
-libmath_la_SOURCES     += $(precomp)/primetab.c
-PRECOMPS               += $(precomp)/primetab.h $(precomp)/primetab.c
+pkginclude_HEADERS     += $(precomp)/math/primetab.h
+nodist_libmath_la_SOURCES += ../precomp/math/primetab.c
+PRECOMPS               += $(precomp)/math/primetab.h \
+                          $(precomp)/math/primetab.c
 PRECOMP_PROGS          += genprimes
 genprimes_LDADD                 = $(mLib_LIBS)
 if !CROSS_COMPILING
-$(precomp)/primetab.h: $(precomp)/primetab.c
-$(precomp)/primetab.c:
-       $(AM_V_at)$(MKDIR_P) $(precomp)
-       $(AM_V_at)$(MAKE) genprimes$e
+$(precomp)/math/primetab.h: $(precomp)/math/primetab.c
+$(precomp)/math/primetab.c:
+       $(AM_V_at)$(MKDIR_P) $(precomp)/math
+       $(AM_V_at)$(MAKE) genprimes$(EXEEXT)
        $(AM_V_GEN)./genprimes -sCATACOMB_PRIMETAB_H \
-               -h$(precomp)/primetab.h -c$(precomp)/primetab.c \
+               -h$(precomp)/math/primetab.h -c$(precomp)/math/primetab.c \
                -n256 -t"unsigned short" -iprimetab
 endif
 
@@ -228,7 +229,7 @@ libmath_la_SOURCES  += pgen.c
 libmath_la_SOURCES     += pgen-gcd.c
 libmath_la_SOURCES     += pgen-simul.c
 libmath_la_SOURCES     += pgen-stdev.c
-TESTS                  += pgen.$t
+TESTS                  += pgen.t$(EXEEXT)
 EXTRA_DIST             += t/pgen
 
 ## Finding primitive elements in finite fields.
@@ -238,8 +239,8 @@ libmath_la_SOURCES  += prim.c
 ## Iterating over all prime numbers from a given starting point.
 pkginclude_HEADERS     += primeiter.h
 libmath_la_SOURCES     += primeiter.c
-TESTS                  += primeiter.$t
-primeiter.lo: $(precomp)/wheel.h
+TESTS                  += primeiter.t$(EXEEXT)
+primeiter.lo: $(precomp)/math/wheel.h
 
 ## The Miller--Rabin primality test.
 pkginclude_HEADERS     += rabin.h
@@ -251,18 +252,18 @@ pkginclude_HEADERS        += strongprime.h
 libmath_la_SOURCES     += strongprime.c
 
 ## A `wheel', used by the prime iteration machinery.
-pkginclude_HEADERS     += $(precomp)/wheel.h
-libmath_la_SOURCES     += $(precomp)/wheel.c
-PRECOMPS               += $(precomp)/wheel.h $(precomp)/wheel.c
+pkginclude_HEADERS     += $(precomp)/math/wheel.h
+nodist_libmath_la_SOURCES += ../precomp/math/wheel.c
+PRECOMPS               += $(precomp)/math/wheel.h $(precomp)/math/wheel.c
 PRECOMP_PROGS          += genwheel
 genwheel_LDADD          = $(mLib_LIBS)
 if !CROSS_COMPILING
-$(precomp)/wheel.h: $(precomp)/wheel.c
-$(precomp)/wheel.c:
-       $(AM_V_at)$(MKDIR_P) $(precomp)
-       $(AM_V_at)$(MAKE) genwheel$e
+$(precomp)/math/wheel.h: $(precomp)/math/wheel.c
+$(precomp)/math/wheel.c:
+       $(AM_V_at)$(MKDIR_P) $(precomp)/math
+       $(AM_V_at)$(MAKE) genwheel$(EXEEXT)
        $(AM_V_GEN)./genwheel -sCATACOMB_WHEEL_H \
-               -h$(precomp)/wheel.h -c$(precomp)/wheel.c \
+               -h$(precomp)/math/wheel.h -c$(precomp)/math/wheel.c \
                -n5 -t"unsigned char" -iwheel
 endif
 
@@ -272,42 +273,44 @@ endif
 ## User-visible binary polynomial arithmetic.
 pkginclude_HEADERS     += gf.h
 libmath_la_SOURCES     += gf-arith.c
-TESTS                  += gf-arith.$t
+TESTS                  += gf-arith.t$(EXEEXT)
 libmath_la_SOURCES     += gf-exp.c gf-exp.h
 libmath_la_SOURCES     += gf-gcd.c
-TESTS                  += gf-gcd.$t
+TESTS                  += gf-gcd.t$(EXEEXT)
 EXTRA_DIST             += t/gf
 
 ## Low-level binary polynomial arithmetic.
 pkginclude_HEADERS     += gfx.h
 libmath_la_SOURCES     += gfx.c
-TESTS                  += gfx.$t
+TESTS                  += gfx.t$(EXEEXT)
 libmath_la_SOURCES     += gfx-kmul.c
-TESTS                  += gfx-kmul.$t
-libmath_la_SOURCES     += gfx-sqr.c $(precomp)/gfx-sqrtab.c
-PRECOMPS               += $(precomp)/gfx-sqrtab.c
+TESTS                  += gfx-kmul.t$(EXEEXT)
+libmath_la_SOURCES     += gfx-sqr.c
+nodist_libmath_la_SOURCES += ../precomp/math/gfx-sqrtab.c
+PRECOMPS               += $(precomp)/math/gfx-sqrtab.c
 PRECOMP_PROGS          += gfx-sqr-mktab
 if !CROSS_COMPILING
-$(precomp)/gfx-sqrtab.c:
-       $(AM_V_at)$(MKDIR_P) $(precomp)
-       $(AM_V_at)$(MAKE) gfx-sqr-mktab$e
-       $(AM_V_GEN)./gfx-sqr-mktab >$(precomp)/gfx-sqrtab.c.new && \
-               mv $(precomp)/gfx-sqrtab.c.new $(precomp)/gfx-sqrtab.c
+$(precomp)/math/gfx-sqrtab.c:
+       $(AM_V_at)$(MKDIR_P) $(precomp)/math
+       $(AM_V_at)$(MAKE) gfx-sqr-mktab$(EXEEXT)
+       $(AM_V_GEN)./gfx-sqr-mktab >$(precomp)/math/gfx-sqrtab.c.new && \
+               mv $(precomp)/math/gfx-sqrtab.c.new \
+                       $(precomp)/math/gfx-sqrtab.c
 endif
-TESTS                  += gfx-sqr.$t
+TESTS                  += gfx-sqr.t$(EXEEXT)
 EXTRA_DIST             += t/gfx
 
 ## Conversions between normal and polynomial basis representations for binary
 ## fields.
 pkginclude_HEADERS     += gfn.h
 libmath_la_SOURCES     += gfn.c
-TESTS                  += gfn.$t
+TESTS                  += gfn.t$(EXEEXT)
 EXTRA_DIST             += t/gfn
 
 ## Efficient reduction modulo sparse polynomials.
 pkginclude_HEADERS     += gfreduce.h
 libmath_la_SOURCES     += gfreduce.c gfreduce-exp.h
-TESTS                  += gfreduce.$t
+TESTS                  += gfreduce.t$(EXEEXT)
 EXTRA_DIST             += t/gfreduce
 
 ###--------------------------------------------------------------------------
@@ -325,7 +328,7 @@ libmath_la_SOURCES  += g-bin.c
 libmath_la_SOURCES     += g-prime.c
 libmath_la_SOURCES     += g-ec.c
 EXTRA_DIST             += group-test.c
-TESTS                  += group-test.$t
+TESTS                  += group-test.t$(EXEEXT)
 EXTRA_DIST             += t/group
 
 ## Abstract finite fields.
@@ -363,11 +366,11 @@ pkginclude_HEADERS        += ec.h ec-guts.h
 libmath_la_SOURCES     += ec.c
 libmath_la_SOURCES     += ec-exp.c ec-exp.h
 libmath_la_SOURCES     += ec-info.c
-TESTS                  += ec-info.$t
+TESTS                  += ec-info.t$(EXEEXT)
 libmath_la_SOURCES     += ec-bin.c
-TESTS                  += ec-bin.$t
+TESTS                  += ec-bin.t$(EXEEXT)
 libmath_la_SOURCES     += ec-prime.c
-TESTS                  += ec-prime.$t
+TESTS                  += ec-prime.t$(EXEEXT)
 EXTRA_DIST             += t/ec
 
 ## The standard `raw' encoding (`EC2OSP') of elliptic curve points.
@@ -381,7 +384,7 @@ libmath_la_SOURCES  += ec-fetch.c
 ## Test infrastructure for elliptic curves.
 pkginclude_HEADERS     += ec-test.h
 libmath_la_SOURCES     += ec-test.c
-TESTS                  += ec-test.$t
+TESTS                  += ec-test.t$(EXEEXT)
 
 ## Table of built-in elliptic-curve groups.
 pkginclude_HEADERS     += ectab.h
index 2d7d635..61780b0 100644 (file)
@@ -116,7 +116,7 @@ mp *mp_lsr2c(mp *d, mp *a, size_t n)
 
 int mp_testbit(mp *x, unsigned long n)
 {
-  if (n > MPW_BITS * MP_LEN(x))
+  if (n >= MPW_BITS * MP_LEN(x))
     return (0);
   return ((x->v[n/MPW_BITS] >> n%MPW_BITS) & 1u);
 }
index bb109b5..ad4309e 100644 (file)
--- a/math/mp.h
+++ b/math/mp.h
@@ -37,6 +37,7 @@
 #include <assert.h>
 #include <string.h>
 
+#include <mLib/macros.h>
 #include <mLib/sub.h>
 
 #ifndef CATACOMB_MPW_H
@@ -196,7 +197,8 @@ extern void mp_destroy(mp */*m*/);
 
 extern mp *mp_copy(mp */*m*/);
 
-#define MP_COPY(m) ((m)->ref++, (m))
+#define MP_COPY(m) MUFFLE_WARNINGS_EXPR(GCC_WARNING("-Wunused-value"), \
+                                       ((m)->ref++, (m)))
 
 /* --- @mp_drop@ --- *
  *
index 54be12a..7fb4c7b 100644 (file)
@@ -28,6 +28,7 @@ include $(top_srcdir)/vars.am
 
 noinst_LTLIBRARIES      = libmisc.la
 libmisc_la_SOURCES      =
+nodist_libmisc_la_SOURCES =
 
 TEST_LIBS               = libmisc.la
 
@@ -37,21 +38,22 @@ TEST_LIBS            = libmisc.la
 ## Efficient sharing over GF(2^8).
 pkginclude_HEADERS     += gfshare.h
 libmisc_la_SOURCES     += gfshare.c
-libmisc_la_SOURCES     += $(precomp)/gfshare-tab.c
-PRECOMPS               += $(precomp)/gfshare-tab.c
+nodist_libmisc_la_SOURCES += ../precomp/misc/gfshare-tab.c
+PRECOMPS               += $(precomp)/misc/gfshare-tab.c
 PRECOMP_PROGS          += gfshare-mktab
 if !CROSS_COMPILING
-$(precomp)/gfshare-tab.c:
-       $(AM_V_at)$(MKDIR_P) $(precomp)
-       $(AM_V_at)$(MAKE) gfshare-mktab$e
-       $(AM_V_GEN)./gfshare-mktab >$(precomp)/gfshare-tab.c.new && \
-               mv $(precomp)/gfshare-tab.c.new $(precomp)/gfshare-tab.c
+$(precomp)/misc/gfshare-tab.c:
+       $(AM_V_at)$(MKDIR_P) $(precomp)/misc
+       $(AM_V_at)$(MAKE) gfshare-mktab$(EXEEXT)
+       $(AM_V_GEN)./gfshare-mktab >$(precomp)/misc/gfshare-tab.c.new && \
+               mv $(precomp)/misc/gfshare-tab.c.new \
+                       $(precomp)/misc/gfshare-tab.c
 endif
-TESTS                  += gfshare.$t
+TESTS                  += gfshare.t$(EXEEXT)
 
 ## Number-theoretic sharing over GF(p).
 pkginclude_HEADERS     += share.h
 libmisc_la_SOURCES     += share.c
-TESTS                  += share.$t
+TESTS                  += share.t$(EXEEXT)
 
 ###----- That's all, folks --------------------------------------------------
index d919edf..b21d6af 100644 (file)
@@ -39,9 +39,9 @@ pkginclude_HEADERS    += bbs.h
 libpub_la_SOURCES      += bbs-fetch.c
 libpub_la_SOURCES      += bbs-gen.c
 libpub_la_SOURCES      += bbs-jump.c
-TESTS                  += bbs-jump.$t
+TESTS                  += bbs-jump.t$(EXEEXT)
 libpub_la_SOURCES      += bbs-rand.c
-TESTS                  += bbs-rand.$t
+TESTS                  += bbs-rand.t$(EXEEXT)
 EXTRA_DIST             += t/bbs
 
 ## Plain Diffie--Hellman, in Schorr groups.
@@ -52,30 +52,30 @@ libpub_la_SOURCES   += dh-gen.c
 libpub_la_SOURCES      += dh-kcdsa.c
 libpub_la_SOURCES      += dh-limlee.c
 libpub_la_SOURCES      += dh-param.c
-TESTS                  += dh-param.$t
+TESTS                  += dh-param.t$(EXEEXT)
 
 ## The National Security Agency's Digital Signature Algorithm.
 pkginclude_HEADERS     += dsa.h
 libpub_la_SOURCES      += dsa-gen.c
-TESTS                  += dsa-gen.$t
+TESTS                  += dsa-gen.t$(EXEEXT)
 libpub_la_SOURCES      += dsa-misc.c
 libpub_la_SOURCES      += dsa-sign.c
-TESTS                  += dsa-sign.$t
+TESTS                  += dsa-sign.t$(EXEEXT)
 libpub_la_SOURCES      += dsa-verify.c
-TESTS                  += dsa-verify.$t
+TESTS                  += dsa-verify.t$(EXEEXT)
 libpub_la_SOURCES      += dsa-check.c
 EXTRA_DIST             += t/dsa
 
 ## Generalization of DSA to arbitrary cyclic groups.
 pkginclude_HEADERS     += gdsa.h
 libpub_la_SOURCES      += gdsa.c
-TESTS                  += gdsa.$t
+TESTS                  += gdsa.t$(EXEEXT)
 EXTRA_DIST             += t/gdsa
 
 ## KISA `KCDSA', generalized to arbitrary cyclic groups.
 pkginclude_HEADERS     += gkcdsa.h
 libpub_la_SOURCES      += gkcdsa.c
-TESTS                  += gkcdsa.$t
+TESTS                  += gkcdsa.t$(EXEEXT)
 EXTRA_DIST             += t/gkcdsa
 
 ## General key validity checking machinery.
@@ -95,7 +95,7 @@ libpub_la_SOURCES     += oaep.c
 libpub_la_SOURCES      += pkcs1.c
 libpub_la_SOURCES      += pss.c
 EXTRA_DIST             += rsa-test.c
-TESTS                  += rsa-test.$t
+TESTS                  += rsa-test.t$(EXEEXT)
 EXTRA_DIST             += t/rsa
 
 ###----- That's all, folks --------------------------------------------------
index 58b62b1..c7dc86e 100644 (file)
@@ -348,7 +348,7 @@ static const grand_ops gops = {
   "bbs",
   GRAND_CRYPTO, 0,
   gmisc, gdestroy,
-  gword, gbyte, gword, grand_range, grand_fill
+  gword, gbyte, gword, grand_defaultrange, grand_defaultfill
 };
 
 /* --- @bbs_rand@ --- *
index 3934131..d97749e 100644 (file)
@@ -53,7 +53,7 @@ librand_la_SOURCES    += grand.c
 ## A simple linear-congruential generator.
 pkginclude_HEADERS     += lcrand.h
 librand_la_SOURCES     += lcrand.c
-TESTS                  += lcrand.$t
+TESTS                  += lcrand.t$(EXEEXT)
 EXTRA_DIST             += t/lcrand
 
 ## Maurer's universal statistical test.
@@ -67,17 +67,18 @@ librand_la_SOURCES  += noise.c
 ## Cryptographic laundering for true random data generation.
 pkginclude_HEADERS     += rand.h
 librand_la_SOURCES     += rand.c
+librand_la_SOURCES     += randgen.c
 
 ## The SSL v3 pseudorandom function.
 pkginclude_HEADERS     += sslprf.h
 librand_la_SOURCES     += sslprf.c
-TESTS                  += sslprf.$t
+TESTS                  += sslprf.t$(EXEEXT)
 EXTRA_DIST             += t/sslprf
 
 ## The TLS v1 pseudorandom function.
 pkginclude_HEADERS     += tlsprf.h
 librand_la_SOURCES     += tlsprf.c
-TESTS                  += tlsprf.$t
+TESTS                  += tlsprf.t$(EXEEXT)
 EXTRA_DIST             += t/tlsprf
 
 ###----- That's all, folks --------------------------------------------------
index 9b18ab0..70ffaf8 100644 (file)
@@ -310,7 +310,8 @@ static const grand_ops gops = {
   "dsarand",
   0, 0,
   gmisc, gdestroy,
-  grand_word, grand_byte, grand_word, grand_range, gfill
+  grand_defaultword, grand_defaultbyte, grand_defaultword,
+  grand_defaultrange, gfill
 };
 
 /* --- @dsarand_create@ --- *
index 17d248b..e445191 100644 (file)
 
 #include "grand.h"
 
+/*----- Default operations ------------------------------------------------*/
+
+/* --- @grand_defaultbyte@ --- *
+ *
+ * Arguments:  @grand *r@ = pointet to generic generator
+ *
+ * Returns:    A uniformly-distributed pseudorandom integer in the interval
+ *             %$[0, 256)$%.
+ *
+ * Use:                Default @byte@ output method.  This calls the @range@ method
+ *             to return a uniform random value between 0 and 255.
+ */
+
+octet grand_defaultbyte(grand *r)
+  { return (r->ops->range(r, 256)); }
+
+/* --- @grand_defaultword@ --- *
+ *
+ * Arguments:  @grand *r@ = pointet to generic generator
+ *
+ * Returns:    A uniformly-distributed pseudorandom integer in the interval
+ *             %$[0, 2^{32})$%.
+ *
+ * Use:                Default @word@ output method.  This calls the @fill@ method
+ *             to fill a 4-octet buffer with uniform random bytes, and then
+ *             converts them to an integer.
+ */
+
+uint32 grand_defaultword(grand *r)
+  { octet buf[4]; r->ops->fill(r, buf, sizeof(buf)); return (LOAD32(buf)); }
+
+/* --- @grand_defaultrange@ --- *
+ *
+ * Arguments:  @grand *r@ = pointet to generic generator
+ *             @uint32 l@ = limit for acceptable results
+ *
+ * Returns:    A uniformly-distributed pseudorandom integer in the interval
+ *             %$[0, l)$%.
+ *
+ * Use:                Default @range@ output method.  This falls back to either
+ *             @word@ (if the generator's @max@ is zero, or if @max < l@) or
+ *             @raw@ (otherwise).  This might recurse via @fill@ and @byte@,
+ *             but this is safe because of the constraint on the @raw@
+ *             method.
+ */
+
+uint32 grand_defaultrange(grand *r, uint32 l)
+{
+  uint32 m, z;
+  uint32 (*w)(grand */*r*/);
+  uint32 x;
+
+  /* --- Decide where to get data from --- *
+   *
+   * The choice of %$2^{32} - 1$% as a limit when using @grand_word@ isn't
+   * wonderful, but working with %$2^{32}$% is awkward and the loss of a few
+   * return values isn't significant.  The algorithm below still successfully
+   * returns uniformly distributed results.
+   *
+   * If there's a raw generator, and it can cope with the limit, then use it;
+   * otherwise use the @word@ generator, which may recurse via @fill@ and
+   * @byte@, but by that point it must be able to satisfy us.
+   */
+
+  if (r->ops->max && r->ops->max >= l) {
+    w = r->ops->raw;
+    m = r->ops->max;
+  } else {
+    assert(!r->ops->max || r->ops->max >= 256);
+    w = grand_word;
+    m = 0xffffffff;
+  }
+
+  /* --- Work out maximum acceptable return value --- *
+   *
+   * This will be the highest multiple of @l@ less than @m@.
+   */
+
+  z = m - m%l;
+
+  /* --- Generate numbers until something acceptable is found --- *
+   *
+   * This will require an expected number of attempts less than 2.
+   */
+
+  do x = w(r); while (x >= z);
+  return (x%l);
+}
+
+/* --- @grand_defaultfill@ --- *
+ *
+ * Arguments:  @grand *r@ = pointet to generic generator
+ *             @void *p@ = pointer to a buffer
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    ---
+ *
+ * Use:                Fills a buffer with uniformly distributed pseudorandom bytes.
+ *             This calls the @byte@ method repeatedly to fill in the output
+ *             buffer.
+ */
+
+void grand_defaultfill(grand *r, void *p, size_t sz)
+  { octet *q = p; while (sz--) *q++ = r->ops->byte(r); }
+
 /*----- Main code ---------------------------------------------------------*/
 
 /* --- @grand_byte@ --- *
 
 octet grand_byte(grand *r)
 {
-  if (r->ops->byte != grand_byte)
-    return (r->ops->byte(r));
-  else if (r->ops->word != grand_word)
-    return (r->ops->word(r) & 0xff);
-  else if (r->ops->fill != grand_fill) {
-    octet o;
-    r->ops->fill(r, &o, 1);
-    return (o);
-  } else
-    return (grand_range(r, 256));
+  if (r->ops->byte == grand_byte) return (grand_defaultbyte(r));
+  else return (r->ops->byte(r));
 }
 
 /* --- @grand_word@ --- *
@@ -67,13 +164,8 @@ octet grand_byte(grand *r)
 
 uint32 grand_word(grand *r)
 {
-  if (r->ops->word != grand_word)
-    return (r->ops->word(r));
-  else {
-    octet b[4];
-    grand_fill(r, b, sizeof(b));
-    return (LOAD32(b));
-  }
+  if (r->ops->word == grand_word) return (grand_defaultword(r));
+  else return (r->ops->word(r));
 }
 
 /* --- @grand_range@ --- *
@@ -87,44 +179,8 @@ uint32 grand_word(grand *r)
 
 uint32 grand_range(grand *r, uint32 l)
 {
-  if (r->ops->range != grand_range)
-    return (r->ops->range(r, l));
-  else {
-    uint32 m, z;
-    uint32 (*w)(grand */*r*/);
-    uint32 x;
-
-    /* --- Decide where to get data from --- *
-     *
-     * The choice of %$2^{32} - 1$% as a limit when using @grand_word@ isn't
-     * wonderful, but working with %$2^{32}$% is awkward and the loss of a
-     * few return values isn't significant.  The algorithm below still
-     * successfully returns uniformly distributed results.
-     */
-
-    if (r->ops->max) {
-      w = r->ops->raw;
-      m = r->ops->max;
-    } else {
-      w = grand_word;
-      m = 0xffffffff;
-    }
-
-    /* --- Work out maximum acceptable return value --- *
-     *
-     * This will be the highest multiple of @l@ less than @m@.
-     */
-
-    z = m - (m % l);
-
-    /* --- Generate numbers until something acceptable is found --- *
-     *
-     * This will require an expected number of attempts less than 2.
-     */
-
-    do x = w(r); while (x >= z);
-    return (x % l);
-  }
+  if (r->ops->range == grand_range) return (grand_defaultrange(r, l));
+  else return (r->ops->range(r, l));
 }
 
 /* --- @grand_fill@ --- *
@@ -141,15 +197,8 @@ uint32 grand_range(grand *r, uint32 l)
 
 void grand_fill(grand *r, void *p, size_t sz)
 {
-  if (r->ops->fill != grand_fill)
-    r->ops->fill(r, p, sz);
-  else {
-    octet *q = p;
-    while (sz) {
-      *q++ = r->ops->byte(r);
-      sz--;
-    }
-  }
+  if (r->ops->fill == grand_fill) grand_defaultfill(r, p, sz);
+  else r->ops->fill(r, p, sz);
 }
 
 /*----- That's all, folks -------------------------------------------------*/
index ec11404..b45f045 100644 (file)
@@ -51,7 +51,10 @@ typedef struct grand_ops {
 
   const char *name;                    /* Generator's name */
   unsigned f;                          /* Various flags */
-  uint32 max;                          /* Maximum raw output */
+  uint32 max;                          /* Maximum raw output, if nonzero;
+                                        * must be either zero or at least
+                                        * 256
+                                        */
 
   /* --- Maintenance methods --- */
 
@@ -60,9 +63,10 @@ typedef struct grand_ops {
 
   /* --- Output methods --- *
    *
-   * Only one of these operations need actually be implemented.  All the
-   * other operations may be synthesized.  Of course, performance is improved
-   * if more are provided.
+   * Of these, only @raw@ need be implemented directly by the generator: the
+   * others can point to provided @grand_default...@ functions, which will
+   * synthesize the necessary behaviour.  Of course, this comes at an
+   * efficiency penalty.
    */
 
   uint32 (*raw)(grand */*r*/);         /* Uniform over %$[0, max)$% */
@@ -105,6 +109,67 @@ enum {
 
 #define GRAND_BADOP assert(((void)"bad grand_misc op", 0))
 
+/*----- Default operations ------------------------------------------------*/
+
+/* --- @grand_defaultbyte@ --- *
+ *
+ * Arguments:  @grand *r@ = pointet to generic generator
+ *
+ * Returns:    A uniformly-distributed pseudorandom integer in the interval
+ *             %$[0, 256)$%.
+ *
+ * Use:                Default @byte@ output method.  This calls the @range@ method
+ *             to return a uniform random value between 0 and 255.
+ */
+
+extern octet grand_defaultbyte(grand */*r*/);
+
+/* --- @grand_defaultword@ --- *
+ *
+ * Arguments:  @grand *r@ = pointet to generic generator
+ *
+ * Returns:    A uniformly-distributed pseudorandom integer in the interval
+ *             %$[0, 2^{32})$%.
+ *
+ * Use:                Default @word@ output method.  This calls the @fill@ method
+ *             to fill a 4-octet buffer with uniform random bytes, and then
+ *             converts them to an integer.
+ */
+
+extern uint32 grand_defaultword(grand */*r*/);
+
+/* --- @grand_defaultrange@ --- *
+ *
+ * Arguments:  @grand *r@ = pointet to generic generator
+ *             @uint32 l@ = limit for acceptable results
+ *
+ * Returns:    A uniformly-distributed pseudorandom integer in the interval
+ *             %$[0, l)$%.
+ *
+ * Use:                Default @range@ output method.  This falls back to either
+ *             @word@ (if the generator's @max@ is zero, or if @max < l@) or
+ *             @raw@ (otherwise).  This might recurse via @fill@ and @byte@,
+ *             but this is safe because of the constraint on the @raw@
+ *             method.
+ */
+
+extern uint32 grand_defaultrange(grand */*r*/, uint32 /*l*/);
+
+/* --- @grand_defaultfill@ --- *
+ *
+ * Arguments:  @grand *r@ = pointet to generic generator
+ *             @void *p@ = pointer to a buffer
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    ---
+ *
+ * Use:                Fills a buffer with uniformly distributed pseudorandom bytes.
+ *             This calls the @byte@ method repeatedly to fill in the output
+ *             buffer.
+ */
+
+extern void grand_defaultfill(grand */*r*/, void */*p*/, size_t /*sz*/);
+
 /*----- Functions provided ------------------------------------------------*/
 
 /* --- @grand_byte@ --- *
index 6944a71..d8c4a37 100644 (file)
@@ -246,7 +246,7 @@ static const grand_ops gops = {
   "lcrand",
   LCRAND_P, 0,
   gmisc, gdestroy,
-  graw, gbyte, grand_word, grange, grand_fill
+  graw, gbyte, grand_defaultword, grange, grand_defaultfill
 };
 
 /* --- @lcrand_create@ --- *
index 3bece70..de120d8 100644 (file)
 
 #include "config.h"
 
+#include <errno.h>
 #include <setjmp.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 
 #include <sys/types.h>
 #include <sys/time.h>
 #  include <grp.h>
 #endif
 
+#if defined(HAVE_LINUX_RANDOM_H)
+#  include <linux/random.h>
+#  include <sys/syscall.h>
+#endif
+
 #include <mLib/bits.h>
 #include <mLib/mdup.h>
 #include <mLib/sel.h>
 /*----- Magical numbers ---------------------------------------------------*/
 
 #define NOISE_KIDLIFE 100000           /* @noise_filter@ child lifetime */
-#define MILLION 1000000                        /* One million */
+
+#if HAVE_CLOCK_GETTIME && _POSIX_TIMERS > 0
+#  define TIMESTRUCT timespec
+#  define tv_SEC tv_sec
+#  define tv_FRAC tv_nsec
+#  define TIMERES 1000000000
+#  if _POSIX_MONOTONIC_CLOCK > 0
+#    define GETTIME(tv) (clock_gettime(CLOCK_MONOTONIC, (tv)))
+#  else
+#    define GETTIME(tv) (clock_gettime(CLOCK_REALTIME, (tv)))
+#  endif
+#  define TOTIMEVAL(tv, xx)                                            \
+       ((tv)->tv_sec = (xx)->tv_sec,                                   \
+        (tv)->tv_usec = ((xx)->tv_nsec + 500)/1000)
+#else
+#  define TIMESTRUCT timeval
+#  define tv_SEC tv_sec
+#  define tv_FRAC tv_usec
+#  define TIMERES 1000000
+#  define GETTIME(tv) (gettimeofday((tv), 0))
+#  define TOTIMEVAL(tv, xx) (*(tv) = *(xx))
+#endif
 
 /*----- Noise source definition -------------------------------------------*/
 
@@ -87,8 +115,8 @@ static gid_t noise_gid = NOISE_NOSETGID; /* Gid to set to spawn processes */
 
 static int bitcount(unsigned long x)
 {
-  char ctab[] = { 0, 1, 1, 2, 1, 2, 2, 3,
-                 1, 2, 2, 3, 2, 3, 3, 4 };
+  static const char ctab[] = { 0, 1, 1, 2, 1, 2, 2, 3,
+                              1, 2, 2, 3, 2, 3, 3, 4 };
   int count = 0;
   while (x) {
     count += ctab[x & 0xfu];
@@ -100,20 +128,20 @@ static int bitcount(unsigned long x)
 /* --- @timer@ --- *
  *
  * Arguments:  @rand_pool *r@ = pointer to randomness pool
- *             @struct timeval *tv@ = pointer to time block
+ *             @const struct TIMESTRUCT *tv@ = pointer to time block
  *
  * Returns:    Nonzero if some randomness was contributed.
  *
  * Use:                Low-level timer contributor.
  */
 
-static int timer(rand_pool *r, struct timeval *tv)
+static int timer(rand_pool *r, const struct TIMESTRUCT *tv)
 {
   unsigned long x, d, dd;
   int de, dde;
   int ret;
 
-  x = tv->tv_usec + MILLION * tv->tv_sec;
+  x = tv->tv_FRAC + TIMERES*tv->tv_SEC;
   d = x ^ noise_last;
   dd = d ^ noise_diff;
   noise_last = x;
@@ -140,9 +168,8 @@ static int timer(rand_pool *r, struct timeval *tv)
 
 int noise_timer(rand_pool *r)
 {
-  struct timeval tv;
-  gettimeofday(&tv, 0);
-  return (timer(r, &tv));
+  struct TIMESTRUCT tv;
+  GETTIME(&tv); return (timer(r, &tv));
 }
 
 /* --- @noise_devrandom@ --- *
@@ -157,11 +184,57 @@ int noise_timer(rand_pool *r)
 
 int noise_devrandom(rand_pool *r)
 {
-  int fd;
+  int fd = -1;
   octet buf[RAND_POOLSZ];
   ssize_t len;
   size_t n = 0;
   int ret = 0;
+#ifdef __linux__
+  fd_set infd;
+  struct timeval tv = { 0, 0 };
+#endif
+#ifdef HAVE_GETENTROPY
+  size_t nn;
+#endif
+
+#if defined(HAVE_LINUX_RANDOM_H) &&                                    \
+    defined(GRND_NONBLOCK) &&                                          \
+    defined(SYS_getrandom)
+  /* --- Use the new shinies if available --- */
+
+  while (n < sizeof(buf)) {
+    if ((len = syscall(SYS_getrandom, buf + n, sizeof(buf) - n,
+                      GRND_NONBLOCK)) <= 0) {
+      if (errno == ENOSYS) break;
+      else goto done;
+    }
+    n += len;
+  }
+  if (n == sizeof(buf)) goto win;
+#endif
+
+#ifdef HAVE_GETENTROPY
+  /* --- OpenBSD-flavoured shinies --- */
+
+  while (n < sizeof(buf)) {
+    nn = sizeof(buf) - nn;
+    if (nn > 256) nn = 256;
+    if (getentropy(buf + n, nn)) break;
+    n += nn;
+  }
+  if (n == sizeof(buf)) goto win;
+#endif
+
+#ifdef __linux__
+  /* --- Don't take from `/dev/urandom' if `/dev/random' would block --- */
+
+  if ((fd = open("/dev/random", O_RDONLY | O_NONBLOCK)) < 0) goto done;
+  FD_ZERO(&infd);
+  FD_SET(fd, &infd);
+  if (select(fd + 1, &infd, 0, 0, &tv) < 0 || !FD_ISSET(fd, &infd))
+    goto done;
+  close(fd); fd = -1;
+#endif
 
   /* --- Be nice to other clients of the random device --- *
    *
@@ -171,18 +244,24 @@ int noise_devrandom(rand_pool *r)
    * needs to get some more entropy from somewhere.
    */
 
-  if ((fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK)) >= 0 ||
+  if (fd >= 0 ||
+      (fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK)) >= 0 ||
       (fd = open("/dev/arandom", O_RDONLY | O_NONBLOCK)) >= 0 ||
       (fd = open("/dev/random", O_RDONLY | O_NONBLOCK)) >= 0) {
     while (n < sizeof(buf)) {
       if ((len = read(fd, buf + n, sizeof(buf) - n)) <= 0) break;
       n += len;
     }
-    rand_add(r, buf, n, n * 8);
-    BURN(buf);
-    if (n == sizeof(buf)) ret = 1;
-    close(fd);
+    if (n == sizeof(buf)) goto win;
   }
+  goto done;
+
+win:
+  ret = 1;
+done:
+  if (fd >= 0) close(fd);
+  rand_add(r, buf, n, 8*n);
+  BURN(buf);
   noise_timer(r);
   return (ret);
 }
@@ -261,6 +340,7 @@ int noise_filter(rand_pool *r, int good, const char *c)
   pid_t kid;
   int fd[2];
   struct timeval dead;
+  struct TIMESTRUCT now;
   int ret = 0;
   struct noisekid nk = { 0 };
   sel_state sel;
@@ -273,8 +353,8 @@ int noise_filter(rand_pool *r, int good, const char *c)
 
   /* --- Remember when this business started --- */
 
-  gettimeofday(&dead, 0);
-  timer(r, &dead);
+  GETTIME(&now); timer(r, &now);
+  TOTIMEVAL(&dead, &now);
 
   /* --- Create a pipe --- */
 
@@ -365,7 +445,7 @@ int noise_filter(rand_pool *r, int good, const char *c)
 
 #ifdef USE_FREEWHEEL
 
-static jmp_buf fwjmp;
+static sigjmp_buf fwjmp;
 
 static void fwalarm(int sig)
 {
index e0bebbf..f9f16d5 100644 (file)
@@ -27,6 +27,8 @@
 
 /*----- Header files ------------------------------------------------------*/
 
+#include "config.h"
+
 #include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
@@ -35,6 +37,7 @@
 #include <mLib/sub.h>
 
 #include "arena.h"
+#include "dispatch.h"
 #include "paranoia.h"
 
 #define RAND__HACKS
@@ -68,7 +71,7 @@ typedef struct rand__gctx {
 
 gctx rand_global = {
   { &gops },
-  { { 0 }, 0, 0, 0,
+  { { 0 }, 0, 0, 0, 0,
     { 0 }, RAND_SECSZ, 0,
     { "Catacomb global random byte pool" },
     &noise_source }
@@ -79,9 +82,15 @@ gctx rand_global = {
 #define RAND_RESOLVE(r)                                                        \
   do { if ((r) == RAND_GLOBAL) r = &rand_global.p; } while (0)
 
-#define TIMER(r) do {                                                  \
-  if ((r)->s && (r)->s->timer)                                         \
-    (r)->s->timer(r);                                                  \
+#define GENCHECK(r) do {                                               \
+  unsigned gen = rand_generation();                                    \
+  if (r->gen != gen) { r->gen = gen; rand_gate(r); }                   \
+} while (0)
+
+static int quick(rand_pool *);
+#define QUICK(r) do {                                                  \
+  quick(r);                                                            \
+  if ((r)->s && (r)->s->timer) (r)->s->timer(r);                       \
 } while (0)
 
 /*----- Main code ---------------------------------------------------------*/
@@ -103,6 +112,7 @@ void rand_init(rand_pool *r)
   RAND_RESOLVE(r);
   memset(r->pool, 0, sizeof(r->pool));
   memset(r->buf, 0, sizeof(r->buf));
+  r->gen = rand_generation();
   r->i = 0;
   r->irot = 0;
   r->ibits = r->obits = 0;
@@ -135,6 +145,47 @@ void rand_noisesrc(rand_pool *r, const rand_source *s)
   r->s = s;
 }
 
+/* --- @rand_quick@ --- *
+ *
+ * Arguments:  @rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:    Zero on success; @-1@ on failure.
+ *
+ * Use         Attempts to use some machine-specific `quick' source of
+ *             entropy to top up @r@.  This may not do anything at all on
+ *             many systems.
+ */
+
+CPU_DISPATCH(static, return, int, quick, (rand_pool *r), (r),
+            pick_quick, trivial_quick);
+
+static int trivial_quick(rand_pool *r) { return (-1); }
+
+#if __GNUC__ && (CPUFAM_X86 || CPUFAM_AMD64)
+static int rdrand_quick(rand_pool *r)
+{
+  unsigned long rr;
+  int i = 16;
+
+  __asm__ ("0: rdrand %0; jc 9f; dec %1; jnz 0b; 9:"
+          : "=r" (rr), "=r" (i) : "1" (i) : "cc");
+  if (!i) return (-1);
+  rand_add(r, &rr, sizeof(rr), 8*sizeof(rr));
+  return (0);
+}
+#endif
+
+static quick__functype *pick_quick(void)
+{
+#if __GNUC__ && (CPUFAM_X86 || CPUFAM_AMD64)
+  DISPATCH_PICK_COND(rand_quick, rdrand_quick,
+                    cpu_feature_p(CPUFEAT_X86_RDRAND));
+#endif
+  DISPATCH_PICK_FALLBACK(rand_quick, trivial_quick);
+}
+
+int rand_quick(rand_pool *r) { RAND_RESOLVE(r); return (quick(r)); }
+
 /* --- @rand_seed@ --- *
  *
  * Arguments:  @rand_pool *r@ = pointer to a randomness pool
@@ -261,16 +312,17 @@ unsigned rand_goodbits(rand_pool *r)
 
 void rand_gate(rand_pool *r)
 {
-  octet h[HASH_SZ];
+  octet h[HASH_SZ], g[4];
   HASH_CTX hc;
   CIPHER_CTX cc;
 
   RAND_RESOLVE(r);
-  TIMER(r);
+  QUICK(r);
 
   /* --- Hash up all the data in the pool --- */
 
   HASH_INIT(&hc);
+  STORE32(g, r->gen); HASH(&hc, g, sizeof(g));
   HASH(&hc, r->pool, RAND_POOLSZ);
   HASH(&hc, r->buf, RAND_BUFSZ);
   HASH_DONE(&hc, h);
@@ -293,7 +345,7 @@ void rand_gate(rand_pool *r)
     r->obits = RAND_OBITS;
   } else
     r->ibits = 0;
-  TIMER(r);
+  QUICK(r);
 }
 
 /* --- @rand_stretch@ --- *
@@ -310,16 +362,17 @@ void rand_gate(rand_pool *r)
 
 void rand_stretch(rand_pool *r)
 {
-  octet h[HASH_SZ];
+  octet h[HASH_SZ], g[4];
   HASH_CTX hc;
   CIPHER_CTX cc;
 
   RAND_RESOLVE(r);
-  TIMER(r);
+  QUICK(r);
 
   /* --- Hash up all the data in the buffer --- */
 
   HASH_INIT(&hc);
+  STORE32(g, r->gen); HASH(&hc, g, sizeof(g));
   HASH(&hc, r->pool, RAND_POOLSZ);
   HASH(&hc, r->buf, RAND_BUFSZ);
   HASH_DONE(&hc, h);
@@ -335,7 +388,7 @@ void rand_stretch(rand_pool *r)
   /* --- Reset the various state variables --- */
 
   r->o = RAND_SECSZ;
-  TIMER(r);
+  QUICK(r);
 }
 
 /* --- @rand_get@ --- *
@@ -359,7 +412,8 @@ void rand_get(rand_pool *r, void *p, size_t sz)
   octet *o = p;
 
   RAND_RESOLVE(r);
-  TIMER(r);
+  GENCHECK(r);
+  QUICK(r);
 
   if (!sz)
     return;
@@ -412,7 +466,8 @@ void rand_getgood(rand_pool *r, void *p, size_t sz)
     rand_get(r, p, sz);
     return;
   }
-  TIMER(r);
+  GENCHECK(r);
+  QUICK(r);
 
   while (sz) {
     size_t chunk = sz;
@@ -514,7 +569,7 @@ static int gmisc(grand *r, unsigned op, ...)
       rand_seed(&g->p, va_arg(ap, unsigned));
       break;
     case RAND_TIMER:
-      TIMER(&g->p);
+      QUICK(&g->p);
       break;
     case RAND_GOODBITS:
       rc = rand_goodbits(&g->p);
@@ -560,7 +615,7 @@ static const grand_ops gops = {
   "rand",
   GRAND_CRYPTO, 0,
   gmisc, gdestroy,
-  gword, gbyte, gword, grand_range, gfill
+  gword, gbyte, gword, grand_defaultrange, gfill
 };
 
 /* --- @rand_create@ --- *
index 3c2fa73..5584c54 100644 (file)
@@ -99,8 +99,9 @@
 
 typedef struct rand_pool {
   octet pool[RAND_POOLSZ];             /* Actual contents of the pool */
-  unsigned i;                          /* Current index into pool */
-  unsigned irot;                       /* Current rotation applied */
+  unsigned gen;                                /* Generation number */
+  unsigned short i;                    /* Current index into pool */
+  unsigned short irot;                 /* Current rotation applied */
   unsigned ibits;                      /* Number of good bits in pool */
   octet buf[RAND_BUFSZ];               /* Random octet output buffer */
   unsigned o;                          /* Current index into buffer */
@@ -132,6 +133,20 @@ typedef struct rand_source {
 
 extern void rand_init(rand_pool */*r*/);
 
+/* --- @rand_generation@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    A nonzero generation number.
+ *
+ * Use:                Returns a generation number for the current process.  Each
+ *             pool has its own number.  If this matches the process number
+ *             then all is well.  If it doesn't match, then the pool needs
+ *             to be cleaned before its next use.
+ */
+
+extern unsigned rand_generation(void);
+
 /* --- @rand_noisesrc@ --- *
  *
  * Arguments:  @rand_pool *r@ = pointer to a randomness pool
@@ -180,6 +195,19 @@ extern void rand_seed(rand_pool */*r*/, unsigned /*bits*/);
 
 extern void rand_key(rand_pool */*r*/, const void */*k*/, size_t /*sz*/);
 
+/* --- @rand_quick@ --- *
+ *
+ * Arguments:  @rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:    Zero on success; @-1@ on failure.
+ *
+ * Use         Attempts to use some machine-specific `quick' source of
+ *             entropy to top up @r@.  This may not do anything at all on
+ *             many systems.
+ */
+
+extern int rand_quick(rand_pool */*r*/);
+
 /* --- @rand_add@ --- *
  *
  * Arguments:  @rand_pool *r@ = pointer to a randomness pool
diff --git a/rand/randgen.c b/rand/randgen.c
new file mode 100644 (file)
index 0000000..7cd2f35
--- /dev/null
@@ -0,0 +1,50 @@
+/* -*-c-*-
+ *
+ * Pool-generation boundary machinery (Unix-specific)
+ *
+ * (c) 2016 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.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <unistd.h>
+
+#include "rand.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @rand_generation@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    A nonzero generation number.
+ *
+ * Use:                Returns a generation number for the current process.  Each
+ *             pool has its own number.  If this matches the process number
+ *             then all is well.  If it doesn't match, then the pool needs
+ *             to be cleaned before its next use.
+ */
+
+unsigned rand_generation(void) { return (getpid()); }
+
+/*----- That's all, folks -------------------------------------------------*/
index 83a4c84..06187b3 100644 (file)
@@ -270,7 +270,7 @@ static const grand_ops grops = {
   "<sslprf-dummy>",
   GRAND_CRYPTO, 0,
   grmisc, grdestroy,
-  grword, grbyte, grword, grand_range, grfill
+  grword, grbyte, grword, grand_defaultrange, grfill
 };
 
 /* ---@sslprf_rand@ --- *
index 3f055b0..fa28faf 100644 (file)
@@ -249,7 +249,7 @@ static const grand_ops dx_grops = {
   "<tlsdx-dummy>",
   GRAND_CRYPTO, 0,
   dx_grmisc, dx_grdestroy,
-  dx_grword, dx_grbyte, dx_grword, grand_range, dx_grfill
+  dx_grword, dx_grbyte, dx_grword, grand_defaultrange, dx_grfill
 };
 
 /* ---@tlsdx_rand@ --- *
@@ -454,7 +454,7 @@ static const grand_ops prf_grops = {
   "<tlsprf-dummy>",
   GRAND_CRYPTO, 0,
   prf_grmisc, prf_grdestroy,
-  prf_grword, prf_grbyte, prf_grword, grand_range, prf_grfill
+  prf_grword, prf_grbyte, prf_grword, grand_defaultrange, prf_grfill
 };
 
 /* ---@tlsprf_rand@ --- *
index 63bf26b..e56b2a4 100644 (file)
@@ -102,22 +102,23 @@ pkginclude_HEADERS        += $(BLKC_H)
 
 ## Schneier's `Blowfish' block cipher.
 BLKCS                  += blowfish
-libsymm_la_SOURCES     += $(precomp)/blowfish-tab.c
-PRECOMPS               += $(precomp)/blowfish-tab.c
+nodist_libsymm_la_SOURCES += ../precomp/symm/blowfish-tab.c
+PRECOMPS               += $(precomp)/symm/blowfish-tab.c
 PRECOMP_PROGS          += blowfish-mktab
 blowfish_mktab_CPPFLAGS         = $(AM_CPPFLAGS) -DQUIET
 if !CROSS_COMPILING
-$(precomp)/blowfish-tab.c:
-       $(AM_V_at)$(MKDIR_P) $(precomp)
-       $(AM_V_at)$(MAKE) blowfish-mktab$e
-       $(AM_V_GEN)./blowfish-mktab >$(precomp)/blowfish-tab.c.new && \
-               mv $(precomp)/blowfish-tab.c.new $(precomp)/blowfish-tab.c
+$(precomp)/symm/blowfish-tab.c:
+       $(AM_V_at)$(MKDIR_P) $(precomp)/symm
+       $(AM_V_at)$(MAKE) blowfish-mktab$(EXEEXT)
+       $(AM_V_GEN)./blowfish-mktab >$(precomp)/symm/blowfish-tab.c.new && \
+               mv $(precomp)/symm/blowfish-tab.c.new \
+                       $(precomp)/symm/blowfish-tab.c
 endif
 
 ## Adams and Tavares' `CAST' block ciphers.
 BLKCS                  += cast128 cast256
 libsymm_la_SOURCES     += cast-s.c cast-sk.c cast-base.h
-cast256.$t: t/cast256
+cast256.t$(EXEEXT): t/cast256
 EXTRA_DIST             += t/cast256.aes
 MAINTAINERCLEANFILES   += $(srcdir)/t/cast256
 t/cast256: t/cast256.aes
@@ -128,15 +129,16 @@ t/cast256: t/cast256.aes
 
 ## IBM's `DES' block cipher, by Feistel, Coppersmith, and others.
 BLKCS                  += des des3
-libsymm_la_SOURCES     += des-base.h $(precomp)/des-tab.c
-PRECOMPS               += $(precomp)/des-tab.c
+libsymm_la_SOURCES     += des-base.h
+nodist_libsymm_la_SOURCES += ../precomp/symm/des-tab.c
+PRECOMPS               += $(precomp)/symm/des-tab.c
 PRECOMP_PROGS          += des-mktab
 if !CROSS_COMPILING
-$(precomp)/des-tab.c:
-       $(AM_V_at)$(MKDIR_P) $(precomp)
-       $(AM_V_at)$(MAKE) des-mktab$e
-       $(AM_V_GEN)./des-mktab >$(precomp)/des-tab.c.new && \
-               mv $(precomp)/des-tab.c.new $(precomp)/des-tab.c
+$(precomp)/symm/des-tab.c:
+       $(AM_V_at)$(MKDIR_P) $(precomp)/symm
+       $(AM_V_at)$(MAKE) des-mktab$(EXEEXT)
+       $(AM_V_GEN)./des-mktab >$(precomp)/symm/des-tab.c.new && \
+               mv $(precomp)/symm/des-tab.c.new $(precomp)/symm/des-tab.c
 endif
 
 ## Rivest's `DESX' variant, with pre- and post-whitening.
@@ -148,17 +150,17 @@ BLKCS                     += idea
 
 ## IBM's `MARS' block cipher.
 BLKCS                  += mars
-libsymm_la_SOURCES     += $(precomp)/mars-tab.c
-PRECOMPS               += $(precomp)/mars-tab.c
+nodist_libsymm_la_SOURCES += ../precomp/symm/mars-tab.c
+PRECOMPS               += $(precomp)/symm/mars-tab.c
 PRECOMP_PROGS          += mars-mktab
 if !CROSS_COMPILING
-$(precomp)/mars-tab.c:
-       $(AM_V_at)$(MKDIR_P) $(precomp)
-       $(AM_V_at)$(MAKE) mars-mktab$e
-       $(AM_V_GEN)./mars-mktab >$(precomp)/mars-tab.c.new && \
-               mv $(precomp)/mars-tab.c.new $(precomp)/mars-tab.c
+$(precomp)/symm/mars-tab.c:
+       $(AM_V_at)$(MKDIR_P) $(precomp)/symm
+       $(AM_V_at)$(MAKE) mars-mktab$(EXEEXT)
+       $(AM_V_GEN)./mars-mktab >$(precomp)/symm/mars-tab.c.new && \
+               mv $(precomp)/symm/mars-tab.c.new $(precomp)/symm/mars-tab.c
 endif
-mars.$t: t/mars
+mars.t$(EXEEXT): t/mars
 EXTRA_DIST             += t/mars.aes
 MAINTAINERCLEANFILES   += $(srcdir)/t/mars
 t/mars: t/mars.aes
@@ -180,17 +182,24 @@ BLKCS                     += rc5
 ## Daemen and Rijmen's `Rijndael' block cipher, selected as AES.
 BLKCS                  += rijndael rijndael192 rijndael256
 libsymm_la_SOURCES     += rijndael-base.h rijndael-base.c
-libsymm_la_SOURCES     += $(precomp)/rijndael-tab.c
-PRECOMPS               += $(precomp)/rijndael-tab.c
+if CPUFAM_X86
+libsymm_la_SOURCES     += rijndael-x86ish-aesni.S
+endif
+if CPUFAM_AMD64
+libsymm_la_SOURCES     += rijndael-x86ish-aesni.S
+endif
+nodist_libsymm_la_SOURCES += ../precomp/symm/rijndael-tab.c
+PRECOMPS               += $(precomp)/symm/rijndael-tab.c
 PRECOMP_PROGS          += rijndael-mktab
 if !CROSS_COMPILING
-$(precomp)/rijndael-tab.c:
-       $(AM_V_at)$(MKDIR_P) $(precomp)
-       $(AM_V_at)$(MAKE) rijndael-mktab$e
-       $(AM_V_GEN)./rijndael-mktab >$(precomp)/rijndael-tab.c.new && \
-               mv $(precomp)/rijndael-tab.c.new $(precomp)/rijndael-tab.c
+$(precomp)/symm/rijndael-tab.c:
+       $(AM_V_at)$(MKDIR_P) $(precomp)/symm
+       $(AM_V_at)$(MAKE) rijndael-mktab$(EXEEXT)
+       $(AM_V_GEN)./rijndael-mktab >$(precomp)/symm/rijndael-tab.c.new && \
+               mv $(precomp)/symm/rijndael-tab.c.new \
+                       $(precomp)/symm/rijndael-tab.c
 endif
-rijndael.$t: t/rijndael
+rijndael.t$(EXEEXT): t/rijndael
 EXTRA_DIST             += t/rijndael.aes
 MAINTAINERCLEANFILES   += $(srcdir)/t/rijndael
 t/rijndael: t/rijndael.aes
@@ -201,16 +210,17 @@ t/rijndael: t/rijndael.aes
 
 ## Massey's `SAFER' block ciphers.
 BLKCS                  += safer safersk
-libsymm_la_SOURCES     += $(precomp)/safer-tab.c
-PRECOMPS               += $(precomp)/safer-tab.c
+nodist_libsymm_la_SOURCES += ../precomp/symm/safer-tab.c
+PRECOMPS               += $(precomp)/symm/safer-tab.c
 PRECOMP_PROGS          += safer-mktab
 STUBS_HDR              += SAFER-SK,safersk,safer
 if !CROSS_COMPILING
-$(precomp)/safer-tab.c:
-       $(AM_V_at)$(MKDIR_P) $(precomp)
-       $(AM_V_at)$(MAKE) safer-mktab$e
-       $(AM_V_GEN)./safer-mktab >$(precomp)/safer-tab.c.new && \
-               mv $(precomp)/safer-tab.c.new $(precomp)/safer-tab.c
+$(precomp)/symm/safer-tab.c:
+       $(AM_V_at)$(MKDIR_P) $(precomp)/symm
+       $(AM_V_at)$(MAKE) safer-mktab$(EXEEXT)
+       $(AM_V_GEN)./safer-mktab >$(precomp)/symm/safer-tab.c.new && \
+               mv $(precomp)/symm/safer-tab.c.new \
+                       $(precomp)/symm/safer-tab.c
 endif
 
 ## Anderson, Biham and Knudsen's `Serpent' block cipher.
@@ -218,7 +228,7 @@ BLKCS                       += serpent
 libsymm_la_SOURCES     += serpent-sbox.h
 check_PROGRAMS         += serpent-check
 TESTS                  += serpent-check
-serpent.$t: t/serpent
+serpent.t$(EXEEXT): t/serpent
 EXTRA_DIST             += t/serpent.aes
 MAINTAINERCLEANFILES   += $(srcdir)/t/serpent
 t/serpent: t/serpent.aes
@@ -234,15 +244,16 @@ libsymm_la_SOURCES        += skipjack-tab.c
 
 ## Daemen and Rijmen's `Square' block cipher.
 BLKCS                  += square
-libsymm_la_SOURCES     += $(precomp)/square-tab.c
-PRECOMPS               += $(precomp)/square-tab.c
+nodist_libsymm_la_SOURCES += ../precomp/symm/square-tab.c
+PRECOMPS               += $(precomp)/symm/square-tab.c
 PRECOMP_PROGS          += square-mktab
 if !CROSS_COMPILING
-$(precomp)/square-tab.c:
-       $(AM_V_at)$(MKDIR_P) $(precomp)
-       $(AM_V_at)$(MAKE) square-mktab$e
-       $(AM_V_GEN)./square-mktab >$(precomp)/square-tab.c.new && \
-               mv $(precomp)/square-tab.c.new $(precomp)/square-tab.c
+$(precomp)/symm/square-tab.c:
+       $(AM_V_at)$(MKDIR_P) $(precomp)/symm
+       $(AM_V_at)$(MAKE) square-mktab$(EXEEXT)
+       $(AM_V_GEN)./square-mktab >$(precomp)/symm/square-tab.c.new && \
+               mv $(precomp)/symm/square-tab.c.new \
+                       $(precomp)/symm/square-tab.c
 endif
 
 ## Wheeler and Needham's `TEA' and `XTEA' block ciphers.
@@ -251,17 +262,18 @@ BLKCS                     += tea xtea
 ## Schneier, Kelsey, Whiting, Wagner, Hall and Ferguson's `Twofish' block
 ## cipher.
 BLKCS                  += twofish
-libsymm_la_SOURCES     += $(precomp)/twofish-tab.c
-PRECOMPS               += $(precomp)/twofish-tab.c
+nodist_libsymm_la_SOURCES += ../precomp/symm/twofish-tab.c
+PRECOMPS               += $(precomp)/symm/twofish-tab.c
 PRECOMP_PROGS          += twofish-mktab
 if !CROSS_COMPILING
-$(precomp)/twofish-tab.c:
-       $(AM_V_at)$(MKDIR_P) $(precomp)
-       $(AM_V_at)$(MAKE) twofish-mktab$e
-       $(AM_V_GEN)./twofish-mktab >$(precomp)/twofish-tab.c.new && \
-               mv $(precomp)/twofish-tab.c.new $(precomp)/twofish-tab.c
+$(precomp)/symm/twofish-tab.c:
+       $(AM_V_at)$(MKDIR_P) $(precomp)/symm
+       $(AM_V_at)$(MAKE) twofish-mktab$(EXEEXT)
+       $(AM_V_GEN)./twofish-mktab >$(precomp)/symm/twofish-tab.c.new && \
+               mv $(precomp)/symm/twofish-tab.c.new \
+                       $(precomp)/symm/twofish-tab.c
 endif
-twofish.$t: t/twofish
+twofish.t$(EXEEXT): t/twofish
 EXTRA_DIST             += t/twofish.aes
 MAINTAINERCLEANFILES   += $(srcdir)/t/twofish
 t/twofish: t/twofish.aes
@@ -316,29 +328,33 @@ STUBS_HDR         += SHA-384,sha384,sha512
 
 ## Anderson and Biham's `Tiger' hash function.
 HASHES                 += tiger
-libsymm_la_SOURCES     += tiger-base.h $(precomp)/tiger-tab.c
-PRECOMPS               += $(precomp)/tiger-tab.c
+libsymm_la_SOURCES     += tiger-base.h
+nodist_libsymm_la_SOURCES += ../precomp/symm/tiger-tab.c
+PRECOMPS               += $(precomp)/symm/tiger-tab.c
 PRECOMP_PROGS          += tiger-mktab
 if !CROSS_COMPILING
-$(precomp)/tiger-tab.c:
-       $(AM_V_at)$(MKDIR_P) $(precomp)
-       $(AM_V_at)$(MAKE) tiger-mktab$e
-       $(AM_V_GEN)./tiger-mktab >$(precomp)/tiger-tab.c.new && \
-               mv $(precomp)/tiger-tab.c.new $(precomp)/tiger-tab.c
+$(precomp)/symm/tiger-tab.c:
+       $(AM_V_at)$(MKDIR_P) $(precomp)/symm
+       $(AM_V_at)$(MAKE) tiger-mktab$(EXEEXT)
+       $(AM_V_GEN)./tiger-mktab >$(precomp)/symm/tiger-tab.c.new && \
+               mv $(precomp)/symm/tiger-tab.c.new \
+                       $(precomp)/symm/tiger-tab.c
 endif
 
 ## Barreto and Rijmen's `Whirlpool' hash function.
 HASHES                 += whirlpool whirlpool256
-libsymm_la_SOURCES     += $(precomp)/whirlpool-tab.c
-PRECOMPS               += $(precomp)/whirlpool-tab.c
+nodist_libsymm_la_SOURCES += ../precomp/symm/whirlpool-tab.c
+PRECOMPS               += $(precomp)/symm/whirlpool-tab.c
 PRECOMP_PROGS          += whirlpool-mktab
 STUBS_HDR              += Whirlpool-256,whirlpool256,whirlpool
 if !CROSS_COMPILING
-$(precomp)/whirlpool-tab.c:
-       $(AM_V_at)$(MKDIR_P) $(precomp)
-       $(AM_V_at)$(MAKE) whirlpool-mktab$e
-       $(AM_V_GEN)./whirlpool-mktab >$(precomp)/whirlpool-tab.c.new && \
-               mv $(precomp)/whirlpool-tab.c.new $(precomp)/whirlpool-tab.c
+$(precomp)/symm/whirlpool-tab.c:
+       $(AM_V_at)$(MKDIR_P) $(precomp)/symm
+       $(AM_V_at)$(MAKE) whirlpool-mktab$(EXEEXT)
+       $(AM_V_GEN)./whirlpool-mktab \
+                       >$(precomp)/symm/whirlpool-tab.c.new && \
+               mv $(precomp)/symm/whirlpool-tab.c.new \
+                       $(precomp)/symm/whirlpool-tab.c
 endif
 
 ## Bellare, Canetti and Krawczyk's `HMAC' mode for message authentication.
@@ -358,14 +374,14 @@ ALL_HASHES                += crc32=gcrc32
 ## Rivest's `RC4' stream cipher.
 pkginclude_HEADERS     += rc4.h
 libsymm_la_SOURCES     += rc4.c
-TESTS                  += rc4.$t
+TESTS                  += rc4.t$(EXEEXT)
 EXTRA_DIST             += t/rc4
 ALL_CIPHERS            += rc4
 
 ## Coppersmith and Rogaway's `SEAL' pseudorandom function.
 pkginclude_HEADERS     += seal.h
 libsymm_la_SOURCES     += seal.c
-TESTS                  += seal.$t
+TESTS                  += seal.t$(EXEEXT)
 EXTRA_DIST             += t/seal
 ALL_CIPHERS            += seal
 
@@ -378,7 +394,16 @@ ALL_CIPHERS                += seal
 EXTRA_DIST             += salsa20-tvconv
 pkginclude_HEADERS     += salsa20.h salsa20-core.h
 libsymm_la_SOURCES     += salsa20.c
-TESTS                  += salsa20.$t
+if CPUFAM_X86
+libsymm_la_SOURCES     += salsa20-x86ish-sse2.S
+endif
+if CPUFAM_AMD64
+libsymm_la_SOURCES     += salsa20-x86ish-sse2.S
+endif
+if CPUFAM_ARMEL
+libsymm_la_SOURCES     += salsa20-arm-neon.S
+endif
+TESTS                  += salsa20.t$(EXEEXT)
 ALL_CIPHERS            += salsa20 salsa2012 salsa208
 ALL_CIPHERS            += xsalsa20 xsalsa2012 xsalsa208
 STUBS_HDR              += Salsa20/12,salsa2012,salsa20
@@ -388,7 +413,7 @@ STUBS_HDR           += XSalsa20/12,xsalsa2012,salsa20
 STUBS_HDR              += XSalsa20/8,xsalsa208,salsa20
 EXTRA_DIST             += t/salsa20
 MAINTAINERCLEANFILES   += t/salsa20
-salsa20.$t: t/salsa20
+salsa20.t$(EXEEXT): t/salsa20
 SALSA20_ESTREAM_TV      = t/salsa20.estream
 SALSA20_ESTREAM_TV     += t/salsa2012.estream
 SALSA20_ESTREAM_TV     += t/salsa208.estream
@@ -404,7 +429,16 @@ t/salsa20: salsa20-tvconv t/salsa20.local $(SALSA20_ESTREAM_TV)
 ## Bernstein's `ChaCha' stream cipher.
 pkginclude_HEADERS     += chacha.h chacha-core.h
 libsymm_la_SOURCES     += chacha.c
-TESTS                  += chacha.$t
+if CPUFAM_X86
+libsymm_la_SOURCES     += chacha-x86ish-sse2.S
+endif
+if CPUFAM_AMD64
+libsymm_la_SOURCES     += chacha-x86ish-sse2.S
+endif
+if CPUFAM_ARMEL
+libsymm_la_SOURCES     += chacha-arm-neon.S
+endif
+TESTS                  += chacha.t$(EXEEXT)
 EXTRA_DIST             += t/chacha
 ALL_CIPHERS            += chacha20 chacha12 chacha8
 ALL_CIPHERS            += xchacha20 xchacha12 xchacha8
@@ -515,6 +549,6 @@ EXTRA_DIST          += $(SYMM_TEST_FILES)
 EXTRA_DIST             += daftstory.h
 
 ## Clean the debris from the `modes' subdirectory.
-CLEANFILES             += modes/*.to modes/*.$t
+CLEANFILES             += modes/*.to modes/*.t$(EXEEXT)
 
 ###----- That's all, folks --------------------------------------------------
index f48ee46..56c8f05 100644 (file)
@@ -467,12 +467,12 @@ static const octet iv[] = IV;                                             \
 static octet ct[sizeof(text)];                                         \
 static octet pt[sizeof(text)];                                         \
                                                                        \
-static void hexdump(const octet *p, size_t sz)                         \
+static void hexdump(const octet *p, size_t sz, size_t off)             \
 {                                                                      \
   const octet *q = p + sz;                                             \
   for (sz = 0; p < q; p++, sz++) {                                     \
     printf("%02x", *p);                                                        \
-    if ((sz + 1) % PRE##_BLKSZ == 0)                                   \
+    if ((off + sz + 1) % PRE##_BLKSZ == 0)                             \
       putchar(':');                                                    \
   }                                                                    \
 }                                                                      \
@@ -513,14 +513,14 @@ int main(void)                                                            \
     } else {                                                           \
       printf("\nError (sz = %lu)\n", (unsigned long)sz);               \
       status = 1;                                                      \
-      printf("\tplaintext      = "); hexdump(text, sz);                        \
-       printf(", "); hexdump(text + sz, rest);                         \
+      printf("\tplaintext      = "); hexdump(text, sz, 0);             \
+       printf(", "); hexdump(text + sz, rest, sz);                     \
        fputc('\n', stdout);                                            \
-      printf("\tciphertext     = "); hexdump(ct, sz);                  \
-       printf(", "); hexdump(ct + sz, rest);                           \
+      printf("\tciphertext     = "); hexdump(ct, sz, 0);               \
+       printf(", "); hexdump(ct + sz, rest, sz);                       \
        fputc('\n', stdout);                                            \
-      printf("\trecovered text = "); hexdump(pt, sz);                  \
-       printf(", "); hexdump(pt + sz, rest);                           \
+      printf("\trecovered text = "); hexdump(pt, sz, 0);               \
+       printf(", "); hexdump(pt + sz, rest, sz);                       \
        fputc('\n', stdout);                                            \
       fputc('\n', stdout);                                             \
     }                                                                  \
index 767f4c4..4f0f38f 100644 (file)
@@ -426,12 +426,12 @@ static const octet iv[] = IV;                                             \
 static octet ct[sizeof(text)];                                         \
 static octet pt[sizeof(text)];                                         \
                                                                        \
-static void hexdump(const octet *p, size_t sz)                         \
+static void hexdump(const octet *p, size_t sz, size_t off)             \
 {                                                                      \
   const octet *q = p + sz;                                             \
   for (sz = 0; p < q; p++, sz++) {                                     \
     printf("%02x", *p);                                                        \
-    if ((sz + 1) % PRE##_BLKSZ == 0)                                   \
+    if ((off + sz + 1) % PRE##_BLKSZ == 0)                             \
       putchar(':');                                                    \
   }                                                                    \
 }                                                                      \
@@ -472,14 +472,14 @@ int main(void)                                                            \
     } else {                                                           \
       printf("\nError (sz = %lu)\n", (unsigned long)sz);               \
       status = 1;                                                      \
-      printf("\tplaintext      = "); hexdump(text, sz);                        \
-       printf(", "); hexdump(text + sz, rest);                         \
+      printf("\tplaintext      = "); hexdump(text, sz, 0);             \
+       printf(", "); hexdump(text + sz, rest, sz);                     \
        fputc('\n', stdout);                                            \
-      printf("\tciphertext     = "); hexdump(ct, sz);                  \
-       printf(", "); hexdump(ct + sz, rest);                           \
+      printf("\tciphertext     = "); hexdump(ct, sz, 0);               \
+       printf(", "); hexdump(ct + sz, rest, sz);                       \
        fputc('\n', stdout);                                            \
-      printf("\trecovered text = "); hexdump(pt, sz);                  \
-       printf(", "); hexdump(pt + sz, rest);                           \
+      printf("\trecovered text = "); hexdump(pt, sz, 0);               \
+       printf(", "); hexdump(pt + sz, rest, sz);                       \
        fputc('\n', stdout);                                            \
       fputc('\n', stdout);                                             \
     }                                                                  \
diff --git a/symm/chacha-arm-neon.S b/symm/chacha-arm-neon.S
new file mode 100644 (file)
index 0000000..5fb0073
--- /dev/null
@@ -0,0 +1,183 @@
+/// -*- mode: asm; asm-comment-char: ?/ -*-
+///
+/// Fancy SIMD implementation of ChaCha for ARM
+///
+/// (c) 2016 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.
+
+///--------------------------------------------------------------------------
+/// External definitions.
+
+#include "config.h"
+#include "asm-common.h"
+
+///--------------------------------------------------------------------------
+/// Main.code.
+
+       .arch   armv7-a
+       .fpu    neon
+       .section .text
+
+FUNC(chacha_core_arm_neon)
+
+       // Arguments are in registers.
+       // r0 is the number of rounds to perform
+       // r1 points to the input matrix
+       // r2 points to the output matrix
+
+       // First job is to slurp the matrix into the SIMD registers.  vldm
+       // and vstm work on word-aligned data, so this is fine.
+       //
+       //      [ 0  1  2  3] (a, q8)
+       //      [ 4  5  6  7] (b, q9)
+       //      [ 8  9 10 11] (c, q10)
+       //      [12 13 14 15] (d, q11)
+       //
+       // We need a copy for later.  Rather than waste time copying them by
+       // hand, we'll use the three-address nature of the instruction set.
+       // But this means that the main loop is offset by a bit.
+       vldmia  r1, {d24-d31}
+
+       // a += b; d ^= a; d <<<= 16
+       vadd.u32 q8, q12, q13
+       veor    q11, q15, q8
+       vshl.u32 q0, q11, #16
+       vshr.u32 q11, q11, #16
+       vorr    q11, q11, q0
+
+       // c += d; b ^= c; b <<<= 12
+       vadd.u32 q10, q14, q11
+       veor    q9, q13, q10
+       vshl.u32 q0, q9, #12
+       vshr.u32 q9, q9, #20
+       vorr    q9, q9, q0
+
+0:
+       // Apply (the rest of) a column quarterround to each of the columns
+       // simultaneously.  Alas, there doesn't seem to be a packed word
+       // rotate, so we have to synthesize it.
+
+       // a += b; d ^= a; d <<<=  8
+       vadd.u32 q8, q8, q9
+       veor    q11, q11, q8
+       vshl.u32 q0, q11, #8
+       vshr.u32 q11, q11, #24
+       vorr    q11, q11, q0
+
+       // c += d; b ^= c; b <<<=  7
+       vadd.u32 q10, q10, q11
+       vext.32 q11, q11, q11, #3
+       veor    q9, q9, q10
+       vext.32 q10, q10, q10, #2
+       vshl.u32 q0, q9, #7
+       vshr.u32 q9, q9, #25
+       vorr    q9, q9, q0
+
+       // The not-quite-transpose conveniently only involves reordering
+       // elements of individual rows, which can be done quite easily.  It
+       // doesn't involve any movement of elements between rows, or even
+       // renaming of the rows.
+       //
+       //      [ 0  1  2  3]           [ 0  1  2  3] (a, q8)
+       //      [ 4  5  6  7]    -->    [ 5  6  7  4] (b, q9)
+       //      [ 8  9 10 11]           [10 11  8  9] (c, q10)
+       //      [12 13 14 15]           [15 12 13 14] (d, q11)
+       //
+       // The reorderings have for the most part been pushed upwards to
+       // reduce delays.
+       vext.32 q9, q9, q9, #1
+
+       // Apply the diagonal quarterround to each of the columns
+       // simultaneously.
+
+       // a += b; d ^= a; d <<<= 16
+       vadd.u32 q8, q8, q9
+       veor    q11, q11, q8
+       vshl.u32 q0, q11, #16
+       vshr.u32 q11, q11, #16
+       vorr    q11, q11, q0
+
+       // c += d; b ^= c; b <<<= 12
+       vadd.u32 q10, q10, q11
+       veor    q9, q9, q10
+       vshl.u32 q0, q9, #12
+       vshr.u32 q9, q9, #20
+       vorr    q9, q9, q0
+
+       // a += b; d ^= a; d <<<=  8
+       vadd.u32 q8, q8, q9
+       veor    q11, q11, q8
+       vshl.u32 q0, q11, #8
+       vshr.u32 q11, q11, #24
+       vorr    q11, q11, q0
+
+       // c += d; b ^= c; b <<<=  7
+       vadd.u32 q10, q10, q11
+       vext.32 q11, q11, q11, #1
+       veor    q9, q9, q10
+       vext.32 q10, q10, q10, #2
+       vshl.u32 q0, q9, #7
+       vshr.u32 q9, q9, #25
+       vorr    q9, q9, q0
+
+       // Finally finish off undoing the transpose, and we're done for this
+       // doubleround.  Again, most of this was done above so we don't have
+       // to wait for the reorderings.
+       vext.32 q9, q9, q9, #3
+
+       // Decrement the loop counter and see if we should go round again.
+       subs    r0, r0, #2
+       bls     9f
+
+       // Do the first part of the next round because this loop is offset.
+
+       // a += b; d ^= a; d <<<= 16
+       vadd.u32 q8, q8, q9
+       veor    q11, q11, q8
+       vshl.u32 q0, q11, #16
+       vshr.u32 q11, q11, #16
+       vorr    q11, q11, q0
+
+       // c += d; b ^= c; b <<<= 12
+       vadd.u32 q10, q10, q11
+       veor    q9, q9, q10
+       vshl.u32 q0, q9, #12
+       vshr.u32 q9, q9, #20
+       vorr    q9, q9, q0
+
+       b       0b
+
+       // Almost there.  Firstly the feedfoward addition.
+9:     vadd.u32 q8, q8, q12
+       vadd.u32 q9, q9, q13
+       vadd.u32 q10, q10, q14
+       vadd.u32 q11, q11, q15
+
+       // And now we write out the result.
+       vstmia  r2, {d16-d23}
+
+       // And with that, we're done.
+       bx      r14
+
+ENDFUNC
+
+///----- That's all, folks --------------------------------------------------
index ad6b05f..1c0efcd 100644 (file)
 
 /* The ChaCha feedforward step, used at the end of the core function.  Here,
  * @y@ contains the original input matrix; @z@ contains the final one, and is
- * updated.  This is the same as Salsa20.
+ * updated.  This is the same as Salsa20, only without the final permutation.
  */
-#define CHACHA_FFWD(z, y) SALSA20_FFWD(z, y)
+#define CHACHA_FFWD(z, y) do {                                         \
+  int _i;                                                              \
+  for (_i = 0; _i < 16; _i++) (z)[_i] += (y)[_i];                      \
+} while (0)
 
 /* Various numbers of rounds, unrolled.  Read from @y@, and write to @z@. */
 #define CHACHA_4R(z, y)                                                        \
diff --git a/symm/chacha-x86ish-sse2.S b/symm/chacha-x86ish-sse2.S
new file mode 100644 (file)
index 0000000..8688996
--- /dev/null
@@ -0,0 +1,260 @@
+/// -*- mode: asm; asm-comment-char: ?/ -*-
+///
+/// Fancy SIMD implementation of ChaCha
+///
+/// (c) 2015 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.
+
+///--------------------------------------------------------------------------
+/// External definitions.
+
+#include "config.h"
+#include "asm-common.h"
+
+///--------------------------------------------------------------------------
+/// Local utilities.
+
+// Magic constants for shuffling.
+#define ROTL 0x93
+#define ROT2 0x4e
+#define ROTR 0x39
+
+///--------------------------------------------------------------------------
+/// Main code.
+
+       .arch pentium4
+       .section .text
+
+FUNC(chacha_core_x86ish_sse2)
+
+       // Initial setup.
+
+#if CPUFAM_X86
+       // Arguments come in on the stack, and will need to be collected.  We
+       // we can get away with just the scratch registers for integer work,
+       // but we'll run out of XMM registers and will need some properly
+       // aligned space which we'll steal from the stack.  I don't trust the
+       // stack pointer's alignment, so I'll have to mask the stack pointer,
+       // which in turn means I'll need to keep track of the old value.
+       // Hence I'm making a full i386-style stack frame here.
+       //
+       // The Windows and SysV ABIs are sufficiently similar that we don't
+       // need to worry about the differences here.
+
+#  define NR ecx
+#  define IN eax
+#  define OUT edx
+#  define SAVE0 xmm5
+#  define SAVE1 xmm6
+#  define SAVE2 xmm7
+#  define SAVE3 [esp]
+
+       push    ebp
+       mov     ebp, esp
+       sub     esp, 16
+       mov     IN, [ebp + 12]
+       mov     OUT, [ebp + 16]
+       and     esp, ~15
+       mov     NR, [ebp + 8]
+#endif
+
+#if CPUFAM_AMD64 && ABI_SYSV
+       // This is nice.  We have plenty of XMM registers, and the arguments
+       // are in useful places.  There's no need to spill anything and we
+       // can just get on with the code.
+
+#  define NR edi
+#  define IN rsi
+#  define OUT rdx
+#  define SAVE0 xmm5
+#  define SAVE1 xmm6
+#  define SAVE2 xmm7
+#  define SAVE3 xmm8
+#endif
+
+#if CPUFAM_AMD64 && ABI_WIN
+       // Arguments come in registers, but they're different between Windows
+       // and everyone else (and everyone else is saner).
+       //
+       // The Windows ABI insists that we preserve some of the XMM
+       // registers, but we want more than we can use as scratch space.  We
+       // only need to save a copy of the input for the feedforward at the
+       // end, so we might as well use memory rather than spill extra
+       // registers.  (We need an extra 8 bytes to align the stack.)
+
+#  define NR ecx
+#  define IN rdx
+#  define OUT r8
+#  define SAVE0 xmm5
+#  define SAVE1 [rsp +  0]
+#  define SAVE2 [rsp + 16]
+#  define SAVE3 [rsp + 32]
+
+       sub     rsp, 48 + 8
+#endif
+
+       // First job is to slurp the matrix into XMM registers.  Be careful:
+       // the input matrix isn't likely to be properly aligned.
+       //
+       //      [ 0  1  2  3] (a, xmm0)
+       //      [ 4  5  6  7] (b, xmm1)
+       //      [ 8  9 10 11] (c, xmm2)
+       //      [12 13 14 15] (d, xmm3)
+       movdqu  xmm0, [IN +  0]
+       movdqu  xmm1, [IN + 16]
+       movdqu  xmm2, [IN + 32]
+       movdqu  xmm3, [IN + 48]
+
+       // Take a copy for later.  This one is aligned properly, by
+       // construction.
+       movdqa  SAVE0, xmm0
+       movdqa  SAVE1, xmm1
+       movdqa  SAVE2, xmm2
+       movdqa  SAVE3, xmm3
+
+0:
+       // Apply a column quarterround to each of the columns simultaneously.
+       // Alas, there doesn't seem to be a packed doubleword rotate, so we
+       // have to synthesize it.
+
+       // a += b; d ^= a; d <<<= 16
+       paddd   xmm0, xmm1
+       pxor    xmm3, xmm0
+       movdqa  xmm4, xmm3
+       pslld   xmm3, 16
+       psrld   xmm4, 16
+       por     xmm3, xmm4
+
+       // c += d; b ^= c; b <<<= 12
+       paddd   xmm2, xmm3
+       pxor    xmm1, xmm2
+       movdqa  xmm4, xmm1
+       pslld   xmm1, 12
+       psrld   xmm4, 20
+       por     xmm1, xmm4
+
+       // a += b; d ^= a; d <<<=  8
+       paddd   xmm0, xmm1
+       pxor    xmm3, xmm0
+       movdqa  xmm4, xmm3
+       pslld   xmm3, 8
+       psrld   xmm4, 24
+       por     xmm3, xmm4
+
+       // c += d; b ^= c; b <<<=  7
+       paddd   xmm2, xmm3
+       pshufd  xmm3, xmm3, ROTL
+       pxor    xmm1, xmm2
+       pshufd  xmm2, xmm2, ROT2
+       movdqa  xmm4, xmm1
+       pslld   xmm1, 7
+       psrld   xmm4, 25
+       por     xmm1, xmm4
+
+       // The not-quite-transpose conveniently only involves reordering
+       // elements of individual rows, which can be done quite easily.  It
+       // doesn't involve any movement of elements between rows, or even
+       // renaming of the rows.
+       //
+       //      [ 0  1  2  3]           [ 0  1  2  3] (a, xmm0)
+       //      [ 4  5  6  7]    -->    [ 5  6  7  4] (b, xmm1)
+       //      [ 8  9 10 11]           [10 11  8  9] (c, xmm2)
+       //      [12 13 14 15]           [15 12 13 14] (d, xmm3)
+       //
+       // The shuffles have quite high latency, so they've mostly been
+       // pushed upwards.  The remaining one can't be moved, though.
+       pshufd  xmm1, xmm1, ROTR
+
+       // Apply the diagonal quarterround to each of the columns
+       // simultaneously.
+
+       // a += b; d ^= a; d <<<= 16
+       paddd   xmm0, xmm1
+       pxor    xmm3, xmm0
+       movdqa  xmm4, xmm3
+       pslld   xmm3, 16
+       psrld   xmm4, 16
+       por     xmm3, xmm4
+
+       // c += d; b ^= c; b <<<= 12
+       paddd   xmm2, xmm3
+       pxor    xmm1, xmm2
+       movdqa  xmm4, xmm1
+       pslld   xmm1, 12
+       psrld   xmm4, 20
+       por     xmm1, xmm4
+
+       // a += b; d ^= a; d <<<=  8
+       paddd   xmm0, xmm1
+       pxor    xmm3, xmm0
+       movdqa  xmm4, xmm3
+       pslld   xmm3, 8
+       psrld   xmm4, 24
+       por     xmm3, xmm4
+
+       // c += d; b ^= c; b <<<=  7
+       paddd   xmm2, xmm3
+       pshufd  xmm3, xmm3, ROTR
+       pxor    xmm1, xmm2
+       pshufd  xmm2, xmm2, ROT2
+       movdqa  xmm4, xmm1
+       pslld   xmm1, 7
+       psrld   xmm4, 25
+       por     xmm1, xmm4
+
+       // Finally, finish off undoing the transpose, and we're done for this
+       // doubleround.  Again, most of this was done above so we don't have
+       // to wait for the shuffles.
+       pshufd  xmm1, xmm1, ROTL
+
+       // Decrement the loop counter and see if we should go round again.
+       sub     NR, 2
+       ja      0b
+
+       // Almost there.  Firstly, the feedforward addition.
+       paddd   xmm0, SAVE0
+       paddd   xmm1, SAVE1
+       paddd   xmm2, SAVE2
+       paddd   xmm3, SAVE3
+
+       // And now we write out the result.  This one won't be aligned
+       // either.
+       movdqu  [OUT +  0], xmm0
+       movdqu  [OUT + 16], xmm1
+       movdqu  [OUT + 32], xmm2
+       movdqu  [OUT + 48], xmm3
+
+       // Tidy things up.
+#if CPUFAM_X86
+       mov     esp, ebp
+       pop     ebp
+#endif
+#if CPUFAM_AMD64 && ABI_WIN
+       add     rsp, 48 + 8
+#endif
+
+       // And with that, we're done.
+       ret
+
+ENDFUNC
+
+///----- That's all, folks --------------------------------------------------
index bd94ffd..cb879e3 100644 (file)
@@ -27,6 +27,8 @@
 
 /*----- Header files ------------------------------------------------------*/
 
+#include "config.h"
+
 #include <stdarg.h>
 
 #include <mLib/bits.h>
@@ -34,6 +36,7 @@
 #include "arena.h"
 #include "chacha.h"
 #include "chacha-core.h"
+#include "dispatch.h"
 #include "gcipher.h"
 #include "grand.h"
 #include "keysz.h"
@@ -59,9 +62,37 @@ const octet chacha_keysz[] = { KSZ_SET, 32, 16, 10, 0 };
  *             the feedforward step.
  */
 
-static void core(unsigned r, const chacha_matrix src, chacha_matrix dest)
+CPU_DISPATCH(static, (void),
+            void, core, (unsigned r, const chacha_matrix src,
+                         chacha_matrix dest),
+            (r, src, dest),
+            pick_core, simple_core);
+
+static void simple_core(unsigned r, const chacha_matrix src,
+                       chacha_matrix dest)
   { CHACHA_nR(dest, src, r); CHACHA_FFWD(dest, src); }
 
+#if CPUFAM_X86 || CPUFAM_AMD64
+extern core__functype chacha_core_x86ish_sse2;
+#endif
+
+#if CPUFAM_ARMEL
+extern core__functype chacha_core_arm_neon;
+#endif
+
+static core__functype *pick_core(void)
+{
+#if CPUFAM_X86 || CPUFAM_AMD64
+  DISPATCH_PICK_COND(chacha_core, chacha_core_x86ish_sse2,
+                    cpu_feature_p(CPUFEAT_X86_SSE2));
+#endif
+#if CPUFAM_ARMEL
+  DISPATCH_PICK_COND(chacha_core, chacha_core_arm_neon,
+                    cpu_feature_p(CPUFEAT_ARM_NEON));
+#endif
+  DISPATCH_PICK_FALLBACK(chacha_core, simple_core);
+}
+
 /* --- @populate@ --- *
  *
  * Arguments:  @chacha_matrix a@ = a matrix to fill in
@@ -672,7 +703,7 @@ static void grdestroy(grand *r)
   static const grand_ops grops_rand_##rr = {                           \
     "chacha" #rr, GRAND_CRYPTO, 0,                                     \
     grmisc, grdestroy, grword,                                         \
-    grbyte, grword, grand_range, grfill                                        \
+    grbyte, grword, grand_defaultrange, grfill                         \
   };                                                                   \
                                                                        \
   grand *chacha##rr##_rand(const void *k, size_t ksz, const void *n)   \
@@ -714,7 +745,7 @@ CHACHA_VARS(DEFGRAND)
   static const grand_ops grxops_rand_##rr = {                          \
     "xchacha" #rr, GRAND_CRYPTO, 0,                                    \
     grmisc, grxdestroy_##rr, grword,                                   \
-    grbyte, grword, grand_range, grfill                                        \
+    grbyte, grword, grand_defaultrange, grfill                         \
   };                                                                   \
                                                                        \
   grand *xchacha##rr##_rand(const void *k, size_t ksz, const void *n)  \
index 2f35323..4553679 100644 (file)
@@ -398,7 +398,7 @@ static const grand_ops grops = {                                    \
   #pre "-counter",                                                     \
   GRAND_CRYPTO, 0,                                                     \
   grmisc, grdestroy,                                                   \
-  grword, grbyte, grword, grand_range, grfill                          \
+  grword, grbyte, grword, grand_defaultrange, grfill                   \
 };                                                                     \
                                                                        \
 /* --- @pre_counterrand@ --- *                                         \
@@ -453,12 +453,12 @@ static const octet iv[] = IV;                                             \
 static octet ct[sizeof(text)];                                         \
 static octet pt[sizeof(text)];                                         \
                                                                        \
-static void hexdump(const octet *p, size_t sz)                         \
+static void hexdump(const octet *p, size_t sz, size_t off)             \
 {                                                                      \
   const octet *q = p + sz;                                             \
   for (sz = 0; p < q; p++, sz++) {                                     \
     printf("%02x", *p);                                                        \
-    if ((sz + 1) % PRE##_BLKSZ == 0)                                   \
+    if ((off + sz + 1) % PRE##_BLKSZ == 0)                             \
       putchar(':');                                                    \
   }                                                                    \
 }                                                                      \
@@ -499,14 +499,14 @@ int main(void)                                                            \
     } else {                                                           \
       printf("\nError (sz = %lu)\n", (unsigned long)sz);               \
       status = 1;                                                      \
-      printf("\tplaintext      = "); hexdump(text, sz);                        \
-       printf(", "); hexdump(text + sz, rest);                         \
+      printf("\tplaintext      = "); hexdump(text, sz, 0);             \
+       printf(", "); hexdump(text + sz, rest, sz);                     \
        fputc('\n', stdout);                                            \
-      printf("\tciphertext     = "); hexdump(ct, sz);                  \
-       printf(", "); hexdump(ct + sz, rest);                           \
+      printf("\tciphertext     = "); hexdump(ct, sz, 0);               \
+       printf(", "); hexdump(ct + sz, rest, sz);                       \
        fputc('\n', stdout);                                            \
-      printf("\trecovered text = "); hexdump(pt, sz);                  \
-       printf(", "); hexdump(pt + sz, rest);                           \
+      printf("\trecovered text = "); hexdump(pt, sz, 0);               \
+       printf(", "); hexdump(pt + sz, rest, sz);                       \
        fputc('\n', stdout);                                            \
       fputc('\n', stdout);                                             \
     }                                                                  \
index ea153a2..9f50292 100644 (file)
@@ -391,12 +391,12 @@ static const octet iv[] = IV;                                             \
 static octet ct[sizeof(text)];                                         \
 static octet pt[sizeof(text)];                                         \
                                                                        \
-static void hexdump(const octet *p, size_t sz)                         \
+static void hexdump(const octet *p, size_t sz, size_t off)             \
 {                                                                      \
   const octet *q = p + sz;                                             \
   for (sz = 0; p < q; p++, sz++) {                                     \
     printf("%02x", *p);                                                        \
-    if ((sz + 1) % PRE##_BLKSZ == 0)                                   \
+    if ((off + sz + 1) % PRE##_BLKSZ == 0)                             \
       putchar(':');                                                    \
   }                                                                    \
 }                                                                      \
@@ -436,14 +436,14 @@ int main(void)                                                            \
     } else {                                                           \
       printf("\nError (sz = %lu)\n", (unsigned long)sz);               \
       status = 1;                                                      \
-      printf("\tplaintext      = "); hexdump(text, sz);                        \
-       printf(", "); hexdump(text + sz, rest);                         \
+      printf("\tplaintext      = "); hexdump(text, sz, 0);             \
+       printf(", "); hexdump(text + sz, rest, sz);                     \
        fputc('\n', stdout);                                            \
-      printf("\tciphertext     = "); hexdump(ct, sz);                  \
-       printf(", "); hexdump(ct + sz, rest);                           \
+      printf("\tciphertext     = "); hexdump(ct, sz, 0);               \
+       printf(", "); hexdump(ct + sz, rest, sz);                       \
        fputc('\n', stdout);                                            \
-      printf("\trecovered text = "); hexdump(pt, sz);                  \
-       printf(", "); hexdump(pt + sz, rest);                           \
+      printf("\trecovered text = "); hexdump(pt, sz, 0);               \
+       printf(", "); hexdump(pt + sz, rest, sz);                       \
        fputc('\n', stdout);                                            \
       fputc('\n', stdout);                                             \
     }                                                                  \
index b5105ed..529428f 100644 (file)
@@ -327,7 +327,7 @@ static const grand_ops grops = {                                    \
   #pre "-mgf",                                                         \
   GRAND_CRYPTO, 0,                                                     \
   grmisc, grdestroy,                                                   \
-  grword, grbyte, grword, grand_range, grfill                          \
+  grword, grbyte, grword, grand_defaultrange, grfill                   \
 };                                                                     \
                                                                        \
 /* --- @pre_mgfrand@ --- *                                             \
index e9e52a7..73fb6db 100644 (file)
@@ -15,14 +15,14 @@ GENMODES_H =
 %repeat
 GENMODES_C += modes/@blkc-@blkcmode.c
 GENMODES_H += modes/@blkc-@blkcmode.h
-SYMM_TESTS += @blkc-@blkcmode.$t
+SYMM_TESTS += modes/@blkc-@blkcmode.t$(EXEEXT)
 %end
 
 ## Hash function modes.
 %repeat
 GENMODES_C += modes/@hash-@hashmode.c
 GENMODES_H += modes/@hash-@hashmode.h
-SYMM_TESTS += @hash-@hashmode.$t
+SYMM_TESTS += modes/@hash-@hashmode.t$(EXEEXT)
 %end
 
 ## Interface and implementation headers for the various modes.
@@ -40,7 +40,7 @@ BLKC_H =
 %repeat
 BLKC_C += @blkc.c
 BLKC_H += @blkc.h
-SYMM_TESTS += @blkc.$t
+SYMM_TESTS += @blkc.t$(EXEEXT)
 %end
 
 ## Hash function interfaces and implementations.
@@ -49,7 +49,7 @@ HASH_H =
 %repeat
 HASH_C += @hash.c
 HASH_H += @hash.h
-SYMM_TESTS += @hash.$t
+SYMM_TESTS += @hash.t$(EXEEXT)
 %end
 
 ## Modes for symmetric encryption.
index 6b1357d..358ee54 100644 (file)
@@ -410,7 +410,7 @@ static const grand_ops grops = {                                    \
   #pre "-ofb",                                                         \
   GRAND_CRYPTO, 0,                                                     \
   grmisc, grdestroy,                                                   \
-  grword, grbyte, grword, grand_range, grfill                          \
+  grword, grbyte, grword, grand_defaultrange, grfill                   \
 };                                                                     \
                                                                        \
 /* --- @pre_ofbrand@ --- *                                             \
@@ -465,12 +465,12 @@ static const octet iv[] = IV;                                             \
 static octet ct[sizeof(text)];                                         \
 static octet pt[sizeof(text)];                                         \
                                                                        \
-static void hexdump(const octet *p, size_t sz)                         \
+static void hexdump(const octet *p, size_t sz, size_t off)             \
 {                                                                      \
   const octet *q = p + sz;                                             \
   for (sz = 0; p < q; p++, sz++) {                                     \
     printf("%02x", *p);                                                        \
-    if ((sz + 1) % PRE##_BLKSZ == 0)                                   \
+    if ((off + sz + 1) % PRE##_BLKSZ == 0)                             \
       putchar(':');                                                    \
   }                                                                    \
 }                                                                      \
@@ -511,14 +511,14 @@ int main(void)                                                            \
     } else {                                                           \
       printf("\nError (sz = %lu)\n", (unsigned long)sz);               \
       status = 1;                                                      \
-      printf("\tplaintext      = "); hexdump(text, sz);                        \
-       printf(", "); hexdump(text + sz, rest);                         \
+      printf("\tplaintext      = "); hexdump(text, sz, 0);             \
+       printf(", "); hexdump(text + sz, rest, sz);                     \
        fputc('\n', stdout);                                            \
-      printf("\tciphertext     = "); hexdump(ct, sz);                  \
-       printf(", "); hexdump(ct + sz, rest);                           \
+      printf("\tciphertext     = "); hexdump(ct, sz, 0);               \
+       printf(", "); hexdump(ct + sz, rest, sz);                       \
        fputc('\n', stdout);                                            \
-      printf("\trecovered text = "); hexdump(pt, sz);                  \
-       printf(", "); hexdump(pt + sz, rest);                           \
+      printf("\trecovered text = "); hexdump(pt, sz, 0);               \
+       printf(", "); hexdump(pt + sz, rest, sz);                       \
        fputc('\n', stdout);                                            \
       fputc('\n', stdout);                                             \
     }                                                                  \
index 38f0097..214dbc1 100644 (file)
@@ -267,7 +267,7 @@ static const grand_ops grops = {
   "rc4",
   GRAND_CRYPTO, 0,
   grmisc, grdestroy,
-  grword, grbyte, grword, grand_range, grfill
+  grword, grbyte, grword, grand_defaultrange, grfill
 };
 
 /* --- @rc4_rand@ --- *
index bfab63a..b0505a6 100644 (file)
 
 /*----- Header files ------------------------------------------------------*/
 
+#include "config.h"
+
 #include <assert.h>
 #include <stdio.h>
 
 #include <mLib/bits.h>
 
 #include "blkc.h"
+#include "dispatch.h"
 #include "gcipher.h"
 #include "rijndael.h"
 #include "rijndael-base.h"
@@ -55,25 +58,14 @@ const octet rijndael_keysz[] = { KSZ_RANGE, RIJNDAEL_KEYSZ, 4, 32, 4 };
  * Use:                Low-level key-scheduling.
  */
 
-void rijndael_setup(rijndael_ctx *k, unsigned nb, const void *buf, size_t sz)
+static void simple_setup(rijndael_ctx *k, unsigned nb,
+                        const void *buf, unsigned nk)
 {
-  unsigned nk, nr, nw;
+  unsigned nr = k->nr, nw;
   unsigned i, j, jj;
   const octet *p;
   uint32 ww;
 
-  /* --- Sort out the key size --- */
-
-  KSZ_ASSERT(rijndael, sz);
-  nk = sz / 4;
-
-  /* --- Select the number of rounds --- */
-
-  nr = (nk > nb ? nk : nb) + 6;
-  if (nr < 10)
-    nr = 10;
-  k->nr = nr;
-
   /* --- Fetch the first key words out --- */
 
   p = buf;
@@ -120,4 +112,42 @@ void rijndael_setup(rijndael_ctx *k, unsigned nb, const void *buf, size_t sz)
     k->wi[i] = k->w[j + jj++];
 }
 
+CPU_DISPATCH(static, EMPTY, void, setup, (rijndael_ctx *k, unsigned nb,
+                                         const void *buf, unsigned nk),
+            (k, nb, buf, nk), pick_setup, simple_setup)
+
+#if CPUFAM_X86 || CPUFAM_AMD64
+extern setup__functype rijndael_setup_x86ish_aesni;
+#endif
+
+static setup__functype *pick_setup(void)
+{
+#if CPUFAM_X86 || CPUFAM_AMD64
+  DISPATCH_PICK_COND(rijndael_setup, rijndael_setup_x86ish_aesni,
+                    cpu_feature_p(CPUFEAT_X86_AESNI));
+#endif
+  DISPATCH_PICK_FALLBACK(rijndael_setup, simple_setup);
+}
+
+void rijndael_setup(rijndael_ctx *k, unsigned nb, const void *buf, size_t sz)
+{
+  unsigned nk, nr;
+
+  /* --- Sort out the key size --- */
+
+  KSZ_ASSERT(rijndael, sz);
+  nk = sz / 4;
+
+  /* --- Select the number of rounds --- */
+
+  nr = (nk > nb ? nk : nb) + 6;
+  if (nr < 10)
+    nr = 10;
+  k->nr = nr;
+
+  /* --- Do the main setup --- */
+
+  setup(k, nb, buf, nk);
+}
+
 /*----- That's all, folks -------------------------------------------------*/
diff --git a/symm/rijndael-x86ish-aesni.S b/symm/rijndael-x86ish-aesni.S
new file mode 100644 (file)
index 0000000..91fcc35
--- /dev/null
@@ -0,0 +1,604 @@
+/// -*- mode: asm; asm-comment-char: ?/ -*-
+///
+/// AESNI-based implementation of Rijndael
+///
+/// (c) 2015 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.
+
+///--------------------------------------------------------------------------
+/// External definitions.
+
+#include "config.h"
+#include "asm-common.h"
+
+///--------------------------------------------------------------------------
+/// External definitions.
+
+       .globl  F(abort)
+       .globl  F(rijndael_rcon)
+
+///--------------------------------------------------------------------------
+/// Local utilities.
+
+// Magic constants for shuffling.
+#define ROTL 0x93
+#define ROT2 0x4e
+#define ROTR 0x39
+
+///--------------------------------------------------------------------------
+/// Main code.
+
+       .arch   .aes
+       .section .text
+
+/// The AESNI instructions implement a little-endian version of AES, but
+/// Catacomb's internal interface presents as big-endian so as to work better
+/// with things like GCM.  We therefore maintain the round keys in
+/// little-endian form, and have to end-swap blocks in and out.
+///
+/// For added amusement, the AESNI instructions don't implement the
+/// larger-block versions of Rijndael, so we have to end-swap the keys if
+/// we're preparing for one of those.
+
+       // Useful constants.
+       .equ    maxrounds, 16           // maximum number of rounds
+       .equ    maxblksz, 32            // maximum block size, in bytes
+       .equ    kbufsz, maxblksz*(maxrounds + 1) // size of a key-schedule buffer
+
+       // Context structure.
+       .equ    nr, 0                   // number of rounds
+       .equ    w, nr + 4               // encryption key words
+       .equ    wi, w + kbufsz          // decryption key words
+
+///--------------------------------------------------------------------------
+/// Key setup.
+
+FUNC(rijndael_setup_x86ish_aesni)
+
+#if CPUFAM_X86
+       // Arguments are on the stack.  We'll need to stack the caller's
+       // register veriables, but we'll manage.
+
+#  define CTX ebp                      // context pointer
+#  define BLKSZ [esp + 24]             // block size
+
+#  define SI esi                       // source pointer
+#  define DI edi                       // destination pointer
+
+#  define KSZ ebx                      // key size
+#  define KSZo ebx                     // ... as address offset
+#  define NKW edx                      // total number of key words
+#  define NKW_NEEDS_REFRESH 1          // ... needs recalculating
+#  define RCON ecx                     // round constants table
+#  define LIM edx                      // limit pointer
+#  define LIMn edx                     // ... as integer offset from base
+
+#  define NR ecx                       // number of rounds
+#  define LRK eax                      // distance to last key
+#  define LRKo eax                     // ... as address offset
+#  define BLKOFF edx                   // block size in bytes
+#  define BLKOFFo edx                  // ... as address offset
+
+       // Stack the caller's registers.
+       push    ebp
+       push    ebx
+       push    esi
+       push    edi
+
+       // Set up our own variables.
+       mov     CTX, [esp + 20]         // context base pointer
+       mov     SI, [esp + 28]          // key material
+       mov     KSZ, [esp + 32]         // key size, in words
+#endif
+
+#if CPUFAM_AMD64 && ABI_SYSV
+       // Arguments are in registers.  We have plenty, but, to be honest,
+       // the initial register allocation is a bit annoying.
+
+#  define CTX r8                       // context pointer
+#  define BLKSZ r9d                    // block size
+
+#  define SI rsi                       // source pointer
+#  define DI rdi                       // destination pointer
+
+#  define KSZ edx                      // key size
+#  define KSZo rdx                     // ... as address offset
+#  define NKW r10d                     // total number of key words
+#  define RCON rdi                     // round constants table
+#  define LIMn ecx                     // limit pointer
+#  define LIM rcx                      // ... as integer offset from base
+
+#  define NR ecx                       // number of rounds
+#  define LRK eax                      // distance to last key
+#  define LRKo rax                     // ... as address offset
+#  define BLKOFF r9d                   // block size in bytes
+#  define BLKOFFo r9                   // ... as address offset
+
+       // Move arguments to more useful places.
+       mov     CTX, rdi                // context base pointer
+       mov     BLKSZ, esi              // block size in words
+       mov     SI, rdx                 // key material
+       mov     KSZ, ecx                // key size, in words
+#endif
+
+#if CPUFAM_AMD64 && ABI_WIN
+       // Arguments are in different registers, and they're a little tight.
+
+#  define CTX r8                       // context pointer
+#  define BLKSZ edx                    // block size
+
+#  define SI rsi                       // source pointer
+#  define DI rdi                       // destination pointer
+
+#  define KSZ r9d                      // key size
+#  define KSZo r9                      // ... as address offset
+#  define NKW r10d                     // total number of key words
+#  define RCON rdi                     // round constants table
+#  define LIMn ecx                     // limit pointer
+#  define LIM rcx                      // ... as integer offset from base
+
+#  define NR ecx                       // number of rounds
+#  define LRK eax                      // distance to last key
+#  define LRKo rax                     // ... as address offset
+#  define BLKOFF edx                   // block size in bytes
+#  define BLKOFFo rdx                  // ... as address offset
+
+       // We'll need the index registers, which belong to the caller in this
+       // ABI.
+       push    rsi
+       push    rdi
+
+       // Move arguments to more useful places.
+       mov     SI, r8                  // key material
+       mov     CTX, rcx                // context base pointer
+#endif
+
+       // The initial round key material is taken directly from the input
+       // key, so copy it over.
+#if CPUFAM_AMD64 && ABI_SYSV
+       // We've been lucky.  We already have a copy of the context pointer
+       // in rdi, and the key size in ecx.
+       add     DI, w
+#else
+       lea     DI, [CTX + w]
+       mov     ecx, KSZ
+#endif
+       rep     movsd
+
+       // Find out other useful things.
+       mov     NKW, [CTX + nr]         // number of rounds
+       add     NKW, 1
+       imul    NKW, BLKSZ              // total key size in words
+#if !NKW_NEEDS_REFRESH
+       // If we can't keep NKW for later, then we use the same register for
+       // it and LIM, so this move is unnecessary.
+       mov     LIMn, NKW
+#endif
+       sub     LIMn, KSZ               // offset by the key size
+
+       // Find the round constants.
+       ldgot   ecx
+       leaext  RCON, rijndael_rcon, ecx
+
+       // Prepare for the main loop.
+       lea     SI, [CTX + w]
+       mov     eax, [SI + 4*KSZo - 4]  // most recent key word
+       lea     LIM, [SI + 4*LIM]       // limit, offset by one key expansion
+
+       // Main key expansion loop.  The first word of each key-length chunk
+       // needs special treatment.
+       //
+       // This is rather tedious because the Intel `AESKEYGENASSIST'
+       // instruction is very strangely shaped.  Firstly, it wants to
+       // operate on vast SSE registers, even though we're data-blocked from
+       // doing more than operation at a time unless we're doing two key
+       // schedules simultaneously -- and even then we can't do more than
+       // two, because the instruction ignores two of its input words
+       // entirely, and produces two different outputs for each of the other
+       // two.  And secondly it insists on taking the magic round constant
+       // as an immediate, so it's kind of annoying if you're not
+       // open-coding the whole thing.  It's much easier to leave that as
+       // zero and XOR in the round constant by hand.
+9:     movd    xmm0, eax
+       pshufd  xmm0, xmm0, ROTR
+       aeskeygenassist xmm1, xmm0, 0
+       pshufd  xmm1, xmm1, ROTL
+       movd    eax, xmm1
+       xor     eax, [SI]
+       xor     al, [RCON]
+       inc     RCON
+       mov     [SI + 4*KSZo], eax
+       add     SI, 4
+       cmp     SI, LIM
+       jae     8f
+
+       // The next three words are simple...
+       xor     eax, [SI]
+       mov     [SI + 4*KSZo], eax
+       add     SI, 4
+       cmp     SI, LIM
+       jae     8f
+
+       // (Word 2...)
+       xor     eax, [SI]
+       mov     [SI + 4*KSZo], eax
+       add     SI, 4
+       cmp     SI, LIM
+       jae     8f
+
+       // (Word 3...)
+       xor     eax, [SI]
+       mov     [SI + 4*KSZo], eax
+       add     SI, 4
+       cmp     SI, LIM
+       jae     8f
+
+       // Word 4.  If the key is /more/ than 6 words long, then we must
+       // apply a substitution here.
+       cmp     KSZ, 5
+       jb      9b
+       cmp     KSZ, 7
+       jb      0f
+       movd    xmm0, eax
+       pshufd  xmm0, xmm0, ROTL
+       aeskeygenassist xmm1, xmm0, 0
+       movd    eax, xmm1
+0:     xor     eax, [SI]
+       mov     [SI + 4*KSZo], eax
+       add     SI, 4
+       cmp     SI, LIM
+       jae     8f
+
+       // (Word 5...)
+       cmp     KSZ, 6
+       jb      9b
+       xor     eax, [SI]
+       mov     [SI + 4*KSZo], eax
+       add     SI, 4
+       cmp     SI, LIM
+       jae     8f
+
+       // (Word 6...)
+       cmp     KSZ, 7
+       jb      9b
+       xor     eax, [SI]
+       mov     [SI + 4*KSZo], eax
+       add     SI, 4
+       cmp     SI, LIM
+       jae     8f
+
+       // (Word 7...)
+       cmp     KSZ, 8
+       jb      9b
+       xor     eax, [SI]
+       mov     [SI + 4*KSZo], eax
+       add     SI, 4
+       cmp     SI, LIM
+       jae     8f
+
+       // Must be done by now.
+       jmp     9b
+
+       // Next job is to construct the decryption keys.  The keys for the
+       // first and last rounds don't need to be mangled, but the remaining
+       // ones do -- and they all need to be reordered too.
+       //
+       // The plan of action, then, is to copy the final encryption round's
+       // keys into place first, then to do each of the intermediate rounds
+       // in reverse order, and finally do the first round.
+       //
+       // Do all of the heavy lifting with SSE registers.  The order we're
+       // doing this in means that it's OK if we read or write too much, and
+       // there's easily enough buffer space for the over-enthusiastic reads
+       // and writes because the context has space for 32-byte blocks, which
+       // is our maximum and an exact fit for two SSE registers.
+8:     mov     NR, [CTX + nr]          // number of rounds
+#if NKW_NEEDS_REFRESH
+       mov     BLKOFF, BLKSZ
+       mov     LRK, NR
+       imul    LRK, BLKOFF
+#else
+       // If we retain NKW, then BLKSZ and BLKOFF are the same register
+       // because we won't need the former again.
+       mov     LRK, NKW
+       sub     LRK, BLKSZ
+#endif
+       lea     DI, [CTX + wi]
+       lea     SI, [CTX + w + 4*LRKo]  // last round's keys
+       shl     BLKOFF, 2               // block size (in bytes now)
+
+       // Copy the last encryption round's keys.
+       movdqu  xmm0, [SI]
+       movdqu  [DI], xmm0
+       cmp     BLKOFF, 16
+       jbe     9f
+       movdqu  xmm0, [SI + 16]
+       movdqu  [DI + 16], xmm0
+
+       // Update the loop variables and stop if we've finished.
+9:     add     DI, BLKOFFo
+       sub     SI, BLKOFFo
+       sub     NR, 1
+       jbe     0f
+
+       // Do another middle round's keys...
+       movdqu  xmm0, [SI]
+       aesimc  xmm0, xmm0
+       movdqu  [DI], xmm0
+       cmp     BLKOFF, 16
+       jbe     9b
+       movdqu  xmm0, [SI + 16]
+       aesimc  xmm0, xmm0
+       movdqu  [DI + 16], xmm0
+       jmp     9b
+
+       // Finally do the first encryption round.
+0:     movdqu  xmm0, [SI]
+       movdqu  [DI], xmm0
+       cmp     BLKOFF, 16
+       jbe     0f
+       movdqu  xmm0, [SI + 16]
+       movdqu  [DI + 16], xmm0
+
+       // If the block size is not exactly four words then we must end-swap
+       // everything.  We can use fancy SSE toys for this.
+0:     cmp     BLKOFF, 16
+       je      0f
+
+       // Find the byte-reordering table.
+       ldgot   ecx
+       movdqa  xmm5, [INTADDR(endswap_tab, ecx)]
+
+#if NKW_NEEDS_REFRESH
+       // Calculate the number of subkey words again.  (It's a good job
+       // we've got a fast multiplier.)
+       mov     NKW, [CTX + nr]
+       add     NKW, 1
+       imul    NKW, BLKSZ
+#endif
+
+       // End-swap the encryption keys.
+       mov     ecx, NKW
+       lea     SI, [CTX + w]
+       call    endswap_block
+
+       // And the decryption keys.
+       mov     ecx, NKW
+       lea     SI, [CTX + wi]
+       call    endswap_block
+
+0:     // All done.
+#if CPUFAM_X86
+       pop     edi
+       pop     esi
+       pop     ebx
+       pop     ebp
+#endif
+#if CPUFAM_AMD64 && ABI_WIN
+       pop     rdi
+       pop     rsi
+#endif
+       ret
+
+       .align  16
+endswap_block:
+       // End-swap ECX words starting at SI.  The end-swapping table is
+       // already loaded into XMM5; and it's OK to work in 16-byte chunks.
+       movdqu  xmm1, [SI]
+       pshufb  xmm1, xmm5
+       movdqu  [SI], xmm1
+       add     SI, 16
+       sub     ecx, 4
+       ja      endswap_block
+       ret
+
+#undef CTX
+#undef BLKSZ
+#undef SI
+#undef DI
+#undef KSZ
+#undef KSZo
+#undef RCON
+#undef LIMn
+#undef LIM
+#undef NR
+#undef LRK
+#undef LRKo
+#undef BLKOFF
+#undef BLKOFFo
+
+ENDFUNC
+
+///--------------------------------------------------------------------------
+/// Encrypting and decrypting blocks.
+
+       .macro  encdec op, aes, koff
+FUNC(rijndael_\op\()_x86ish_aesni)
+
+       // Find the magic endianness-swapping table.
+       ldgot   ecx
+       movdqa  xmm5, [INTADDR(endswap_tab, ecx)]
+
+#if CPUFAM_X86
+       // Arguments come in on the stack, and need to be collected.  We
+       // don't have a shortage of registers.
+
+#  define K ecx
+#  define SRC edx
+#  define DST edx
+#  define NR eax
+
+       mov     K, [esp + 4]
+       mov     SRC, [esp + 8]
+#endif
+
+#if CPUFAM_AMD64 && ABI_SYSV
+       // Arguments come in registers.  All is good.
+
+#  define K rdi
+#  define SRC rsi
+#  define DST rdx
+#  define NR eax
+#endif
+
+#if CPUFAM_AMD64 && ABI_WIN
+       // Arguments come in different registers.
+
+#  define K rcx
+#  define SRC rdx
+#  define DST r8
+#  define NR eax
+#endif
+
+       // Initial setup.
+       movdqu  xmm0, [SRC]
+       pshufb  xmm0, xmm5
+       mov     NR, [K + nr]
+       add     K, \koff
+
+       // Initial whitening.
+       movdqu  xmm1, [K]
+       add     K, 16
+       pxor    xmm0, xmm1
+
+       // Dispatch to the correct code.
+       cmp     NR, 10
+       je      10f
+       jb      bogus
+       cmp     NR, 14
+       je      14f
+       ja      bogus
+       cmp     NR, 12
+       je      12f
+       jb      11f
+       jmp     13f
+
+       .align  2
+
+       // 14 rounds...
+14:    movdqu  xmm1, [K]
+       add     K, 16
+       \aes    xmm0, xmm1
+
+       // 13 rounds...
+13:    movdqu  xmm1, [K]
+       add     K, 16
+       \aes    xmm0, xmm1
+
+       // 12 rounds...
+12:    movdqu  xmm1, [K]
+       add     K, 16
+       \aes    xmm0, xmm1
+
+       // 11 rounds...
+11:    movdqu  xmm1, [K]
+       add     K, 16
+       \aes    xmm0, xmm1
+
+       // 10 rounds...
+10:    movdqu  xmm1, [K]
+       \aes    xmm0, xmm1
+
+       // 9 rounds...
+       movdqu  xmm1, [K + 16]
+       \aes    xmm0, xmm1
+
+       // 8 rounds...
+       movdqu  xmm1, [K + 32]
+       \aes    xmm0, xmm1
+
+       // 7 rounds...
+       movdqu  xmm1, [K + 48]
+       \aes    xmm0, xmm1
+
+       // 6 rounds...
+       movdqu  xmm1, [K + 64]
+       \aes    xmm0, xmm1
+
+       // 5 rounds...
+       movdqu  xmm1, [K + 80]
+       \aes    xmm0, xmm1
+
+       // 4 rounds...
+       movdqu  xmm1, [K + 96]
+       \aes    xmm0, xmm1
+
+       // 3 rounds...
+       movdqu  xmm1, [K + 112]
+       \aes    xmm0, xmm1
+
+       // 2 rounds...
+       movdqu  xmm1, [K + 128]
+       \aes    xmm0, xmm1
+
+       // Final round...
+       movdqu  xmm1, [K + 144]
+       \aes\()last xmm0, xmm1
+
+       // Unpermute the ciphertext block and store it.
+       pshufb  xmm0, xmm5
+#if CPUFAM_X86
+       mov     DST, [esp + 12]
+#endif
+       movdqu  [DST], xmm0
+
+       // And we're done.
+       ret
+
+#undef K
+#undef SRC
+#undef DST
+#undef NR
+
+ENDFUNC
+       .endm
+
+       encdec  eblk, aesenc, w
+       encdec  dblk, aesdec, wi
+
+///--------------------------------------------------------------------------
+/// Random utilities.
+
+       .align  16
+       // Abort the process because of a programming error.  Indirecting
+       // through this point serves several purposes: (a) by CALLing, rather
+       // than branching to, `abort', we can save the return address, which
+       // might at least provide a hint as to what went wrong; (b) we don't
+       // have conditional CALLs (and they'd be big anyway); and (c) we can
+       // write a HLT here as a backstop against `abort' being mad.
+bogus: callext F(abort)
+0:     hlt
+       jmp     0b
+
+       gotaux  ecx
+
+///--------------------------------------------------------------------------
+/// Data tables.
+
+       .align  16
+endswap_tab:
+       .byte    3,  2,  1,  0
+       .byte    7,  6,  5,  4
+       .byte   11, 10,  9,  8
+       .byte   15, 14, 13, 12
+
+///----- That's all, folks --------------------------------------------------
index 9d8e739..293f28d 100644 (file)
 
 /*----- Header files ------------------------------------------------------*/
 
+#include "config.h"
+
 #include <assert.h>
 #include <stdio.h>
 
 #include <mLib/bits.h>
 
 #include "blkc.h"
+#include "dispatch.h"
 #include "gcipher.h"
 #include "rijndael.h"
 #include "rijndael-base.h"
@@ -69,6 +72,39 @@ void rijndael_init(rijndael_ctx *k, const void *buf, size_t sz)
  * Use:                Low-level block encryption and decryption.
  */
 
+CPU_DISPATCH(EMPTY, EMPTY, void, rijndael_eblk, (const rijndael_ctx *k,
+                                                const uint32 s[4],
+                                                uint32 d[4]),
+            (k, s, d), pick_eblk, simple_eblk)
+
+CPU_DISPATCH(EMPTY, EMPTY, void, rijndael_dblk, (const rijndael_ctx *k,
+                                                const uint32 s[4],
+                                                uint32 d[4]),
+            (k, s, d), pick_dblk, simple_dblk)
+
+#if CPUFAM_X86 || CPUFAM_AMD64
+extern rijndael_eblk__functype rijndael_eblk_x86ish_aesni;
+extern rijndael_dblk__functype rijndael_dblk_x86ish_aesni;
+#endif
+
+static rijndael_eblk__functype *pick_eblk(void)
+{
+#if CPUFAM_X86 || CPUFAM_AMD64
+  DISPATCH_PICK_COND(rijndael_eblk, rijndael_eblk_x86ish_aesni,
+                    cpu_feature_p(CPUFEAT_X86_AESNI));
+#endif
+  DISPATCH_PICK_FALLBACK(rijndael_eblk, simple_eblk);
+}
+
+static rijndael_dblk__functype *pick_dblk(void)
+{
+#if CPUFAM_X86 || CPUFAM_AMD64
+  DISPATCH_PICK_COND(rijndael_dblk, rijndael_dblk_x86ish_aesni,
+                    cpu_feature_p(CPUFEAT_X86_AESNI));
+#endif
+  DISPATCH_PICK_FALLBACK(rijndael_dblk, simple_dblk);
+}
+
 #define DO(what, t, aa, bb, cc, dd, a, b, c, d, w) do {                        \
   aa = what(t, a, b, c, d) ^ *w++;                                     \
   bb = what(t, b, c, d, a) ^ *w++;                                     \
@@ -83,7 +119,7 @@ void rijndael_init(rijndael_ctx *k, const void *buf, size_t sz)
   dd = what(t, d, c, b, a) ^ *w++;                                     \
 } while (0)
 
-void rijndael_eblk(const rijndael_ctx *k, const uint32 *s, uint32 *dst)
+static void simple_eblk(const rijndael_ctx *k, const uint32 *s, uint32 *dst)
 {
   uint32 a = s[0], b = s[1], c = s[2], d = s[3];
   uint32 aa, bb, cc, dd;
@@ -118,7 +154,7 @@ void rijndael_eblk(const rijndael_ctx *k, const uint32 *s, uint32 *dst)
   dst[0] = a; dst[1] = b; dst[2] = c; dst[3] = d;
 }
 
-void rijndael_dblk(const rijndael_ctx *k, const uint32 *s, uint32 *dst)
+static void simple_dblk(const rijndael_ctx *k, const uint32 *s, uint32 *dst)
 {
   uint32 a = s[0], b = s[1], c = s[2], d = s[3];
   uint32 aa, bb, cc, dd;
diff --git a/symm/salsa20-arm-neon.S b/symm/salsa20-arm-neon.S
new file mode 100644 (file)
index 0000000..cea942d
--- /dev/null
@@ -0,0 +1,241 @@
+/// -*- mode: asm; asm-comment-char: ?/ -*-
+///
+/// Fancy SIMD implementation of Salsa20 for ARM
+///
+/// (c) 2016 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.
+
+///--------------------------------------------------------------------------
+/// External definitions.
+
+#include "config.h"
+#include "asm-common.h"
+
+///--------------------------------------------------------------------------
+/// Main.code.
+
+       .arch   armv7-a
+       .fpu    neon
+       .section .text
+
+FUNC(salsa20_core_arm_neon)
+
+       // Arguments are in registers.
+       // r0 is the number of rounds to perform
+       // r1 points to the input matrix
+       // r2 points to the output matrix
+
+       // First job is to slurp the matrix into the SIMD registers.  The
+       // words have already been permuted conveniently to make them line up
+       // better for SIMD processing.
+       //
+       // The textbook arrangement of the matrix is this.
+       //
+       //      [C K K K]
+       //      [K C N N]
+       //      [T T C K]
+       //      [K K K C]
+       //
+       // But we've rotated the columns up so that the main diagonal with
+       // the constants on it end up in the first row, giving something more
+       // like
+       //
+       //      [C C C C]
+       //      [K T K K]
+       //      [T K K N]
+       //      [K K N K]
+       //
+       // so the transformation looks like this:
+       //
+       //      [ 0  1  2  3]           [ 0  5 10 15] (a, q8)
+       //      [ 4  5  6  7]    -->    [ 4  9 14  3] (b, q9)
+       //      [ 8  9 10 11]           [ 8 13  2  7] (c, q10)
+       //      [12 13 14 15]           [12  1  6 11] (d, q11)
+       //
+       //      [ 0  1  2  3] (a, q8)
+       //      [ 4  5  6  7] (b, q9)
+       //      [ 8  9 10 11] (c, q10)
+       //      [12 13 14 15] (d, q11)
+       //
+       // We need a copy for later.  Rather than waste time copying them by
+       // hand, we'll use the three-address nature of the instruction set.
+       // But this means that the main loop is offset by a bit.
+       vldmia  r1, {d24-d31}
+
+       // Apply a column quarterround to each of the columns simultaneously,
+       // moving the results to their working registers.  Alas, there
+       // doesn't seem to be a packed word rotate, so we have to synthesize
+       // it.
+
+       // b ^= (a + d) <<<  7
+       vadd.u32 q0, q12, q15
+       vshl.u32 q1, q0, #7
+       vshr.u32 q0, q0, #25
+       vorr    q0, q0, q1
+       veor    q9, q13, q0
+
+       // c ^= (b + a) <<<  9
+       vadd.u32 q0, q9, q12
+       vshl.u32 q1, q0, #9
+       vshr.u32 q0, q0, #23
+       vorr    q0, q0, q1
+       veor    q10, q14, q0
+
+       // d ^= (c + b) <<< 13
+       vadd.u32 q0, q10, q9
+       vext.32 q9, q9, q9, #3
+       vshl.u32 q1, q0, #13
+       vshr.u32 q0, q0, #19
+       vorr    q0, q0, q1
+       veor    q11, q15, q0
+
+       // a ^= (d + c) <<< 18
+       vadd.u32 q0, q11, q10
+       vext.32 q10, q10, q10, #2
+       vext.32 q11, q11, q11, #1
+       vshl.u32 q1, q0, #18
+       vshr.u32 q0, q0, #14
+       vorr    q0, q0, q1
+       veor    q8, q12, q0
+
+0:
+       // The transpose conveniently only involves reordering elements of
+       // individual rows, which can be done quite easily, and reordering
+       // the rows themselves, which is a trivial renaming.  It doesn't
+       // involve any movement of elements between rows.
+       //
+       //      [ 0  5 10 15]           [ 0  5 10 15] (a, q8)
+       //      [ 4  9 14  3]    -->    [ 1  6 11 12] (b, q11)
+       //      [ 8 13  2  7]           [ 2  7  8 13] (c, q10)
+       //      [12  1  6 11]           [ 3  4  9 14] (d, q9)
+       //
+       // The reorderings have been pushed upwards to reduce delays.
+
+       // Apply the row quarterround to each of the columns (yes!)
+       // simultaneously.
+
+       // b ^= (a + d) <<<  7
+       vadd.u32 q0, q8, q9
+       vshl.u32 q1, q0, #7
+       vshr.u32 q0, q0, #25
+       vorr    q0, q0, q1
+       veor    q11, q11, q0
+
+       // c ^= (b + a) <<<  9
+       vadd.u32 q0, q11, q8
+       vshl.u32 q1, q0, #9
+       vshr.u32 q0, q0, #23
+       vorr    q0, q0, q1
+       veor    q10, q10, q0
+
+       // d ^= (c + b) <<< 13
+       vadd.u32 q0, q10, q11
+       vext.32 q11, q11, q11, #3
+       vshl.u32 q1, q0, #13
+       vshr.u32 q0, q0, #19
+       vorr    q0, q0, q1
+       veor    q9, q9, q0
+
+       // a ^= (d + c) <<< 18
+       vadd.u32 q0, q9, q10
+       vext.32 q10, q10, q10, #2
+       vext.32 q9, q9, q9, #1
+       vshl.u32 q1, q0, #18
+       vshr.u32 q0, q0, #14
+       vorr    q0, q0, q1
+       veor    q8, q8, q0
+
+       // We had to undo the transpose ready for the next loop.  Again, push
+       // back the reorderings to reduce latency.  Decrement the loop
+       // counter and see if we should go round again.
+       subs    r0, r0, #2
+       bls     9f
+
+       // Do the first half of the next round because this loop is offset.
+
+       // b ^= (a + d) <<<  7
+       vadd.u32 q0, q8, q11
+       vshl.u32 q1, q0, #7
+       vshr.u32 q0, q0, #25
+       vorr    q0, q0, q1
+       veor    q9, q9, q0
+
+       // c ^= (b + a) <<<  9
+       vadd.u32 q0, q9, q8
+       vshl.u32 q1, q0, #9
+       vshr.u32 q0, q0, #23
+       vorr    q0, q0, q1
+       veor    q10, q10, q0
+
+       // d ^= (c + b) <<< 13
+       vadd.u32 q0, q10, q9
+       vext.32 q9, q9, q9, #3
+       vshl.u32 q1, q0, #13
+       vshr.u32 q0, q0, #19
+       vorr    q0, q0, q1
+       veor    q11, q11, q0
+
+       // a ^= (d + c) <<< 18
+       vadd.u32 q0, q11, q10
+       vext.32 q10, q10, q10, #2
+       vext.32 q11, q11, q11, #1
+       vshl.u32 q1, q0, #18
+       vshr.u32 q0, q0, #14
+       vorr    q0, q0, q1
+       veor    q8, q8, q0
+
+       b       0b
+
+       // Almost there.  Firstly the feedfoward addition, and then we have
+       // to write out the result.  Here we have to undo the permutation
+       // which was already applied to the input.
+9:     vadd.u32 q8, q8, q12
+       vadd.u32 q9, q9, q13
+       vadd.u32 q10, q10, q14
+       vadd.u32 q11, q11, q15
+
+       vst1.32 {d16[0]}, [r2 :32]!
+       vst1.32 {d22[1]}, [r2 :32]!
+       vst1.32 {d21[0]}, [r2 :32]!
+       vst1.32 {d19[1]}, [r2 :32]!
+
+       vst1.32 {d18[0]}, [r2 :32]!
+       vst1.32 {d16[1]}, [r2 :32]!
+       vst1.32 {d23[0]}, [r2 :32]!
+       vst1.32 {d21[1]}, [r2 :32]!
+
+       vst1.32 {d20[0]}, [r2 :32]!
+       vst1.32 {d18[1]}, [r2 :32]!
+       vst1.32 {d17[0]}, [r2 :32]!
+       vst1.32 {d23[1]}, [r2 :32]!
+
+       vst1.32 {d22[0]}, [r2 :32]!
+       vst1.32 {d20[1]}, [r2 :32]!
+       vst1.32 {d19[0]}, [r2 :32]!
+       vst1.32 {d17[1]}, [r2 :32]!
+
+       // And with that, we're done.
+       bx      r14
+
+ENDFUNC
+
+///----- That's all, folks --------------------------------------------------
index 98efa72..b27f222 100644 (file)
 
 /*----- The Salsa20 core function -----------------------------------------*/
 
+/* It makes life somewhat easier if we don't actually store and maintain the
+ * input matrix in the textbook order.  Instead, we rotate the columns other
+ * than the leftmost one upwards, so that the constants which were originally
+ * along the diagonal end up on the top row.  We'll need to undo this
+ * permutation on output, but that's not too terrible an imposition.
+ *
+ * The permutation we're applying to the matrix elements is this:
+ *
+ * [  0  1  2  3 ]      [  0  5 10 15 ]
+ * [  4  5  6  7 ]  -->  [  4  9 14  3 ]
+ * [  8  9 10 11 ]      [  8 13  2  7 ]
+ * [ 12 13 14 15 ]      [ 12  1  6 11 ]
+ *
+ * and as a result, we need to apply this inverse permutation to figure out
+ * which indices to use in the doublerow function and elsewhere.
+ *
+ * [  0 13 10  7 ]
+ * [  4  1 14 11 ]
+ * [  8  5  2 15 ]
+ * [ 12  9  6  3 ]
+ */
+
 /* The Salsa20 quarter-round.  Read from the matrix @y@ at indices @a@, @b@,
  * @c@, and @d@; and write to the corresponding elements of @z@.
  */
  */
 #define SALSA20_DR(z, y) do {                                          \
   SALSA20_QR(z, y,  0,  4,  8, 12);                                    \
-  SALSA20_QR(z, y,  5,  9, 13,  1);                                    \
-  SALSA20_QR(z, y, 10, 14,  2,  6);                                    \
-  SALSA20_QR(z, y, 15,  3,  7, 11);                                    \
-  SALSA20_QR(z, z,  0,  1,  2,  3);                                    \
-  SALSA20_QR(z, z,  5,  6,  7,  4);                                    \
-  SALSA20_QR(z, z, 10, 11,  8,  9);                                    \
-  SALSA20_QR(z, z, 15, 12, 13, 14);                                    \
+  SALSA20_QR(z, y,  1,  5,  9, 13);                                    \
+  SALSA20_QR(z, y,  2,  6, 10, 14);                                    \
+  SALSA20_QR(z, y,  3,  7, 11, 15);                                    \
+  SALSA20_QR(z, z,  0, 13, 10,  7);                                    \
+  SALSA20_QR(z, z,  1, 14, 11,  4);                                    \
+  SALSA20_QR(z, z,  2, 15,  8,  5);                                    \
+  SALSA20_QR(z, z,  3, 12,  9,  6);                                    \
 } while (0)
 
 /* The Salsa20 feedforward step, used at the end of the core function.  Here,
  * @y@ contains the original input matrix; @z@ contains the final one, and is
- * updated.
+ * updated.  The output is rendered in canonical order, ready for output.
  */
 #define SALSA20_FFWD(z, y) do {                                                \
-  int _i;                                                              \
-  for (_i = 0; _i < 16; _i++) (z)[_i] += (y)[_i];                      \
+  const uint32 *_y = (y);                                              \
+  uint32 *_z = (z);                                                    \
+  int _t;                                                              \
+  _z[ 0] = _z[ 0] + _y[ 0]; _z[ 4] = _z[ 4] + _y[ 4];                  \
+  _z[ 8] = _z[ 8] + _y[ 8]; _z[12] = _z[12] + _y[12];                  \
+      _t = _z[ 1] + _y[ 1]; _z[ 1] = _z[13] + _y[13];                  \
+  _z[13] = _z[ 9] + _y[ 9]; _z[ 9] = _z[ 5] + _y[ 5]; _z[ 5] = _t;     \
+      _t = _z[ 2] + _y[ 2]; _z[ 2] = _z[10] + _y[10]; _z[10] = _t;     \
+      _t = _z[ 6] + _y[ 6]; _z[ 6] = _z[14] + _y[14]; _z[14] = _t;     \
+      _t = _z[ 3] + _y[ 3]; _z[ 3] = _z[ 7] + _y[ 7];                  \
+  _z[ 7] = _z[11] + _y[11]; _z[11] = _z[15] + _y[15]; _z[15] = _t;     \
 } while (0)
 
 /* Various numbers of rounds, unrolled.  Read from @y@, and write to @z@. */
 
 /* Step the counter in the Salsa20 state matrix @a@. */
 #define SALSA20_STEP(a)                                                        \
-  do { (a)[8] = U32((a)[8] + 1); (a)[9] += !(a)[8]; } while (0)
+  do { (a)[8] = U32((a)[8] + 1); (a)[5] += !(a)[8]; } while (0)
 
 /*----- Buffering and output ----------------------------------------------*
  *
diff --git a/symm/salsa20-x86ish-sse2.S b/symm/salsa20-x86ish-sse2.S
new file mode 100644 (file)
index 0000000..d74836b
--- /dev/null
@@ -0,0 +1,331 @@
+/// -*- mode: asm; asm-comment-char: ?/ -*-
+///
+/// Fancy SIMD implementation of Salsa20
+///
+/// (c) 2015 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.
+
+///--------------------------------------------------------------------------
+/// External definitions.
+
+#include "config.h"
+#include "asm-common.h"
+
+///--------------------------------------------------------------------------
+/// Local utilities.
+
+// Magic constants for shuffling.
+#define ROTL 0x93
+#define ROT2 0x4e
+#define ROTR 0x39
+
+///--------------------------------------------------------------------------
+/// Main code.
+
+       .arch pentium4
+       .section .text
+
+FUNC(salsa20_core_x86ish_sse2)
+
+       // Initial setup.
+
+#if CPUFAM_X86
+       // Arguments come in on the stack, and will need to be collected.  We
+       // we can get away with just the scratch registers for integer work,
+       // but we'll run out of XMM registers and will need some properly
+       // aligned space which we'll steal from the stack.  I don't trust the
+       // stack pointer's alignment, so I'll have to mask the stack pointer,
+       // which in turn means I'll need to keep track of the old value.
+       // Hence I'm making a full i386-style stack frame here.
+       //
+       // The Windows and SysV ABIs are sufficiently similar that we don't
+       // need to worry about the differences here.
+
+#  define NR ecx
+#  define IN eax
+#  define OUT edx
+#  define SAVE0 xmm6
+#  define SAVE1 xmm7
+#  define SAVE2 [esp + 0]
+#  define SAVE3 [esp + 16]
+
+       push    ebp
+       mov     ebp, esp
+       sub     esp, 32
+       mov     IN, [ebp + 12]
+       mov     OUT, [ebp + 16]
+       and     esp, ~15
+       mov     NR, [ebp + 8]
+#endif
+
+#if CPUFAM_AMD64 && ABI_SYSV
+       // This is nice.  We have plenty of XMM registers, and the arguments
+       // are in useful places.  There's no need to spill anything and we
+       // can just get on with the code.
+
+#  define NR edi
+#  define IN rsi
+#  define OUT rdx
+#  define SAVE0 xmm6
+#  define SAVE1 xmm7
+#  define SAVE2 xmm8
+#  define SAVE3 xmm9
+#endif
+
+#  if CPUFAM_AMD64 && ABI_WIN
+       // Arguments come in registers, but they're different between Windows
+       // and everyone else (and everyone else is saner).
+       //
+       // The Windows ABI insists that we preserve some of the XMM
+       // registers, but we want more than we can use as scratch space.  Two
+       // places we only need to save a copy of the input for the
+       // feedforward at the end; but the other two we want for the final
+       // permutation, so save the old values on the stack (We need an extra
+       // 8 bytes to align the stack.)
+
+#  define NR ecx
+#  define IN rdx
+#  define OUT r8
+#  define SAVE0 xmm6
+#  define SAVE1 xmm7
+#  define SAVE2 [rsp + 32]
+#  define SAVE3 [rsp + 48]
+
+       sub     rsp, 64 + 8
+       movdqa  [rsp +  0], xmm6
+       movdqa  [rsp + 16], xmm7
+#endif
+
+       // First job is to slurp the matrix into XMM registers.  The words
+       // have already been permuted conveniently to make them line up
+       // better for SIMD processing.
+       //
+       // The textbook arrangement of the matrix is this.
+       //
+       //      [C K K K]
+       //      [K C N N]
+       //      [T T C K]
+       //      [K K K C]
+       //
+       // But we've rotated the columns up so that the main diagonal with
+       // the constants on it end up in the first row, giving something more
+       // like
+       //
+       //      [C C C C]
+       //      [K T K K]
+       //      [T K K N]
+       //      [K K N K]
+       //
+       // so the transformation looks like this:
+       //
+       //      [ 0  1  2  3]           [ 0  5 10 15] (a, xmm0)
+       //      [ 4  5  6  7]    -->    [ 4  9 14  3] (b, xmm1)
+       //      [ 8  9 10 11]           [ 8 13  2  7] (c, xmm2)
+       //      [12 13 14 15]           [12  1  6 11] (d, xmm3)
+       movdqu  xmm0, [IN +  0]
+       movdqu  xmm1, [IN + 16]
+       movdqu  xmm2, [IN + 32]
+       movdqu  xmm3, [IN + 48]
+
+       // Take a copy for later.
+       movdqa  SAVE0, xmm0
+       movdqa  SAVE1, xmm1
+       movdqa  SAVE2, xmm2
+       movdqa  SAVE3, xmm3
+
+0:
+       // Apply a column quarterround to each of the columns simultaneously.
+       // Alas, there doesn't seem to be a packed doubleword rotate, so we
+       // have to synthesize it.
+
+       // b ^= (a + d) <<<  7
+       movdqa  xmm4, xmm0
+       paddd   xmm4, xmm3
+       movdqa  xmm5, xmm4
+       pslld   xmm4, 7
+       psrld   xmm5, 25
+       por     xmm4, xmm5
+       pxor    xmm1, xmm4
+
+       // c ^= (b + a) <<<  9
+       movdqa  xmm4, xmm1
+       paddd   xmm4, xmm0
+       movdqa  xmm5, xmm4
+       pslld   xmm4, 9
+       psrld   xmm5, 23
+       por     xmm4, xmm5
+       pxor    xmm2, xmm4
+
+       // d ^= (c + b) <<< 13
+       movdqa  xmm4, xmm2
+       paddd   xmm4, xmm1
+       pshufd  xmm1, xmm1, ROTL
+       movdqa  xmm5, xmm4
+       pslld   xmm4, 13
+       psrld   xmm5, 19
+       por     xmm4, xmm5
+       pxor    xmm3, xmm4
+
+       // a ^= (d + c) <<< 18
+       movdqa  xmm4, xmm3
+       pshufd  xmm3, xmm3, ROTR
+       paddd   xmm4, xmm2
+       pshufd  xmm2, xmm2, ROT2
+       movdqa  xmm5, xmm4
+       pslld   xmm4, 18
+       psrld   xmm5, 14
+       por     xmm4, xmm5
+       pxor    xmm0, xmm4
+
+       // The transpose conveniently only involves reordering elements of
+       // individual rows, which can be done quite easily, and reordering
+       // the rows themselves, which is a trivial renaming.  It doesn't
+       // involve any movement of elements between rows.
+       //
+       //      [ 0  5 10 15]           [ 0  5 10 15] (a, xmm0)
+       //      [ 4  9 14  3]    -->    [ 1  6 11 12] (b, xmm3)
+       //      [ 8 13  2  7]           [ 2  7  8 13] (c, xmm2)
+       //      [12  1  6 11]           [ 3  4  9 14] (d, xmm1)
+       //
+       // The shuffles have quite high latency, so they've been pushed
+       // backwards into the main instruction list.
+
+       // Apply the row quarterround to each of the columns (yes!)
+       // simultaneously.
+
+       // b ^= (a + d) <<<  7
+       movdqa  xmm4, xmm0
+       paddd   xmm4, xmm1
+       movdqa  xmm5, xmm4
+       pslld   xmm4, 7
+       psrld   xmm5, 25
+       por     xmm4, xmm5
+       pxor    xmm3, xmm4
+
+       // c ^= (b + a) <<<  9
+       movdqa  xmm4, xmm3
+       paddd   xmm4, xmm0
+       movdqa  xmm5, xmm4
+       pslld   xmm4, 9
+       psrld   xmm5, 23
+       por     xmm4, xmm5
+       pxor    xmm2, xmm4
+
+       // d ^= (c + b) <<< 13
+       movdqa  xmm4, xmm2
+       paddd   xmm4, xmm3
+       pshufd  xmm3, xmm3, ROTL
+       movdqa  xmm5, xmm4
+       pslld   xmm4, 13
+       psrld   xmm5, 19
+       por     xmm4, xmm5
+       pxor    xmm1, xmm4
+
+       // a ^= (d + c) <<< 18
+       movdqa  xmm4, xmm1
+       pshufd  xmm1, xmm1, ROTR
+       paddd   xmm4, xmm2
+       pshufd  xmm2, xmm2, ROT2
+       movdqa  xmm5, xmm4
+       pslld   xmm4, 18
+       psrld   xmm5, 14
+       por     xmm4, xmm5
+       pxor    xmm0, xmm4
+
+       // We had to undo the transpose ready for the next loop.  Again, push
+       // back the shuffles because they take a long time coming through.
+       // Decrement the loop counter and see if we should go round again.
+       // Later processors fuse this pair into a single uop.
+       sub     NR, 2
+       ja      0b
+
+       // Almost there.  Firstly, the feedforward addition, and then we have
+       // to write out the result.  Here we have to undo the permutation
+       // which was already applied to the input.  Shuffling has quite high
+       // latency, so arrange to start a new shuffle into a temporary as
+       // soon as we've written out the old value.
+       paddd   xmm0, SAVE0
+       pshufd  xmm4, xmm0, 0x39
+       movd    [OUT +  0], xmm0
+
+       paddd   xmm1, SAVE1
+       pshufd  xmm5, xmm1, ROTL
+       movd    [OUT + 16], xmm1
+
+       paddd   xmm2, SAVE2
+       pshufd  xmm6, xmm2, ROT2
+       movd    [OUT + 32], xmm2
+
+       paddd   xmm3, SAVE3
+       pshufd  xmm7, xmm3, ROTR
+       movd    [OUT + 48], xmm3
+
+       movd    [OUT +  4], xmm7
+       pshufd  xmm7, xmm3, ROT2
+       movd    [OUT + 24], xmm7
+       pshufd  xmm3, xmm3, ROTL
+       movd    [OUT + 44], xmm3
+
+       movd    [OUT +  8], xmm6
+       pshufd  xmm6, xmm2, ROTL
+       movd    [OUT + 28], xmm6
+       pshufd  xmm2, xmm2, ROTR
+       movd    [OUT + 52], xmm2
+
+       movd    [OUT + 12], xmm5
+       pshufd  xmm5, xmm1, ROTR
+       movd    [OUT + 36], xmm5
+       pshufd  xmm1, xmm1, ROT2
+       movd    [OUT + 56], xmm1
+
+       movd    [OUT + 20], xmm4
+       pshufd  xmm4, xmm0, ROT2
+       movd    [OUT + 40], xmm4
+       pshufd  xmm0, xmm0, ROTL
+       movd    [OUT + 60], xmm0
+
+       // Tidy things up.
+
+#if CPUFAM_X86
+       mov     esp, ebp
+       pop     ebp
+#endif
+#if CPUFAM_AMD64 && ABI_WIN
+       movdqa  xmm6, [rsp +  0]
+       movdqa  xmm7, [rsp + 16]
+       add     rsp, 64 + 8
+#endif
+
+       // And with that, we're done.
+       ret
+
+#undef NR
+#undef IN
+#undef OUT
+#undef SAVE0
+#undef SAVE1
+#undef SAVE2
+#undef SAVE3
+
+ENDFUNC
+
+///----- That's all, folks --------------------------------------------------
index c12945e..0afad2c 100644 (file)
@@ -7,11 +7,14 @@
 
 /*----- Header files ------------------------------------------------------*/
 
+#include "config.h"
+
 #include <stdarg.h>
 
 #include <mLib/bits.h>
 
 #include "arena.h"
+#include "dispatch.h"
 #include "gcipher.h"
 #include "grand.h"
 #include "keysz.h"
@@ -39,9 +42,37 @@ const octet salsa20_keysz[] = { KSZ_SET, 32, 16, 10, 0 };
  *             the feedforward step.
  */
 
-static void core(unsigned r, const salsa20_matrix src, salsa20_matrix dest)
+CPU_DISPATCH(static, (void),
+            void, core, (unsigned r, const salsa20_matrix src,
+                         salsa20_matrix dest),
+            (r, src, dest),
+            pick_core, simple_core);
+
+static void simple_core(unsigned r, const salsa20_matrix src,
+                       salsa20_matrix dest)
   { SALSA20_nR(dest, src, r); SALSA20_FFWD(dest, src); }
 
+#if CPUFAM_X86 || CPUFAM_AMD64
+extern core__functype salsa20_core_x86ish_sse2;
+#endif
+
+#if CPUFAM_ARMEL
+extern core__functype salsa20_core_arm_neon;
+#endif
+
+static core__functype *pick_core(void)
+{
+#if CPUFAM_X86 || CPUFAM_AMD64
+  DISPATCH_PICK_COND(salsa20_core, salsa20_core_x86ish_sse2,
+                    cpu_feature_p(CPUFEAT_X86_SSE2));
+#endif
+#if CPUFAM_ARMEL
+  DISPATCH_PICK_COND(salsa20_core, salsa20_core_arm_neon,
+                    cpu_feature_p(CPUFEAT_ARM_NEON));
+#endif
+  DISPATCH_PICK_FALLBACK(salsa20_core, simple_core);
+}
+
 /* --- @populate@ --- *
  *
  * Arguments:  @salsa20_matrix a@ = a matrix to fill in
@@ -61,33 +92,42 @@ static void populate(salsa20_matrix a, const void *key, size_t ksz)
 
   KSZ_ASSERT(salsa20, ksz);
 
-  a[ 1] = LOAD32_L(k +  0);
-  a[ 2] = LOAD32_L(k +  4);
+  /* Here's the pattern of key, constant, nonce, and counter pieces in the
+   * matrix, before and after our permutation.
+   *
+   * [ C0 K0 K1 K2 ]      [ C0 C1 C2 C3 ]
+   * [ K3 C1 N0 N1 ]  -->  [ K3 T1 K7 K2 ]
+   * [ T0 T1 C2 K4 ]      [ T0 K6 K1 N1 ]
+   * [ K5 K6 K7 C3 ]      [ K5 K0 N0 K4 ]
+   */
+
+  a[13] = LOAD32_L(k +  0);
+  a[10] = LOAD32_L(k +  4);
   if (ksz == 10) {
-    a[ 3] = LOAD16_L(k +  8);
+    a[ 7] = LOAD16_L(k +  8);
     a[ 4] = 0;
   } else {
-    a[ 3] = LOAD32_L(k +  8);
+    a[ 7] = LOAD32_L(k +  8);
     a[ 4] = LOAD32_L(k + 12);
   }
   if (ksz <= 16) {
-    a[11] = a[ 1];
-    a[12] = a[ 2];
-    a[13] = a[ 3];
-    a[14] = a[ 4];
+    a[15] = a[13];
+    a[12] = a[10];
+    a[ 9] = a[ 7];
+    a[ 6] = a[ 4];
     a[ 0] = SALSA20_A128;
-    a[ 5] = SALSA20_B128;
-    a[10] = ksz == 10 ? SALSA20_C80 : SALSA20_C128;
-    a[15] = SALSA20_D128;
+    a[ 1] = SALSA20_B128;
+    a[ 2] = ksz == 10 ? SALSA20_C80 : SALSA20_C128;
+    a[ 3] = SALSA20_D128;
   } else {
-    a[11] = LOAD32_L(k + 16);
+    a[15] = LOAD32_L(k + 16);
     a[12] = LOAD32_L(k + 20);
-    a[13] = LOAD32_L(k + 24);
-    a[14] = LOAD32_L(k + 28);
+    a[ 9] = LOAD32_L(k + 24);
+    a[ 6] = LOAD32_L(k + 28);
     a[ 0] = SALSA20_A256;
-    a[ 5] = SALSA20_B256;
-    a[10] = SALSA20_C256;
-    a[15] = SALSA20_D256;
+    a[ 1] = SALSA20_B256;
+    a[ 2] = SALSA20_C256;
+    a[ 3] = SALSA20_D256;
   }
 }
 
@@ -130,8 +170,8 @@ void salsa20_setnonce(salsa20_ctx *ctx, const void *nonce)
 {
   const octet *n = nonce;
 
-  ctx->a[6] = LOAD32_L(n + 0);
-  ctx->a[7] = LOAD32_L(n + 4);
+  ctx->a[14] = LOAD32_L(n + 0);
+  ctx->a[11] = LOAD32_L(n + 4);
   salsa20_seek(ctx, 0);
 }
 
@@ -153,7 +193,7 @@ void salsa20_seek(salsa20_ctx *ctx, unsigned long i)
 
 void salsa20_seeku64(salsa20_ctx *ctx, kludge64 i)
 {
-  ctx->a[8] = LO64(i); ctx->a[9] = HI64(i);
+  ctx->a[8] = LO64(i); ctx->a[5] = HI64(i);
   ctx->bufi = SALSA20_OUTSZ;
 }
 
@@ -169,7 +209,7 @@ unsigned long salsa20_tell(salsa20_ctx *ctx)
   { kludge64 i = salsa20_tellu64(ctx); return (GET64(unsigned long, i)); }
 
 kludge64 salsa20_tellu64(salsa20_ctx *ctx)
-  { kludge64 i; SET64(i, ctx->a[9], ctx->a[8]); return (i); }
+  { kludge64 i; SET64(i, ctx->a[5], ctx->a[8]); return (i); }
 
 /* --- @salsa20{,12,8}_encrypt@ --- *
  *
@@ -272,10 +312,10 @@ SALSA20_VARS(DEFENCRYPT)
      * speed critical, so we do it the harder way.                     \
      */                                                                        \
                                                                        \
-    for (i = 0; i < 4; i++) k[i + 6] = src[i];                         \
+    for (i = 0; i < 4; i++) k[14 - 3*i] = src[i];                      \
     core(r, k, a);                                                     \
-    for (i = 0; i < 4; i++) dest[i] = a[5*i] - k[5*i];                 \
-    for (i = 4; i < 8; i++) dest[i] = a[i + 2] - k[i + 2];             \
+    for (i = 0; i < 4; i++) dest[i] = a[5*i] - k[i];                   \
+    for (i = 4; i < 8; i++) dest[i] = a[i + 2] - k[26 - 3*i];          \
   }                                                                    \
                                                                        \
   void HSALSA20_PRF(r, salsa20_ctx *ctx, const void *src, void *dest)  \
@@ -340,9 +380,9 @@ SALSA20_VARS(DEFHSALSA20)
                                                                        \
     populate(ctx->k, key, ksz);                                                \
     ctx->s.a[ 0] = SALSA20_A256;                                       \
-    ctx->s.a[ 5] = SALSA20_B256;                                       \
-    ctx->s.a[10] = SALSA20_C256;                                       \
-    ctx->s.a[15] = SALSA20_D256;                                       \
+    ctx->s.a[ 1] = SALSA20_B256;                                       \
+    ctx->s.a[ 2] = SALSA20_C256;                                       \
+    ctx->s.a[ 3] = SALSA20_D256;                                       \
     XSALSA20_SETNONCE(r, ctx, nonce ? nonce : zerononce);              \
   }
 SALSA20_VARS(DEFXINIT)
@@ -371,8 +411,8 @@ SALSA20_VARS(DEFXINIT)
                                                                        \
     for (i = 0; i < 4; i++) in[i] = LOAD32_L(n + 4*i);                 \
     HSALSA20_RAW(r, ctx->k, in, out);                                  \
-    for (i = 0; i < 4; i++) ctx->s.a[i + 1] = out[i];                  \
-    for (i = 4; i < 8; i++) ctx->s.a[i + 7] = out[i];                  \
+    for (i = 0; i < 4; i++) ctx->s.a[13 - 3*i] = out[i];               \
+    for (i = 4; i < 8; i++) ctx->s.a[27 - 3*i] = out[i];               \
     salsa20_setnonce(&ctx->s, n + 16);                                 \
   }
 SALSA20_VARS(DEFXNONCE)
@@ -663,7 +703,7 @@ static void grdestroy(grand *r)
   static const grand_ops grops_rand_##rr = {                           \
     SALSA20_NAME_##rr, GRAND_CRYPTO, 0,                                        \
     grmisc, grdestroy, grword,                                         \
-    grbyte, grword, grand_range, grfill                                        \
+    grbyte, grword, grand_defaultrange, grfill                         \
   };                                                                   \
                                                                        \
   grand *SALSA20_DECOR(salsa20, rr, _rand)                             \
@@ -706,7 +746,7 @@ SALSA20_VARS(DEFGRAND)
   static const grand_ops grxops_rand_##rr = {                          \
     "x" SALSA20_NAME_##rr, GRAND_CRYPTO, 0,                            \
     grmisc, grxdestroy_##rr, grword,                                   \
-    grbyte, grword, grand_range, grfill                                        \
+    grbyte, grword, grand_defaultrange, grfill                         \
   };                                                                   \
                                                                        \
   grand *SALSA20_DECOR(xsalsa20, rr, _rand)                            \
@@ -730,23 +770,31 @@ SALSA20_VARS(DEFXGRAND)
 #include <mLib/quis.h>
 #include <mLib/testrig.h>
 
+static const int perm[] = {
+   0, 13, 10,  7,
+   4,  1, 14, 11,
+   8,  5,  2, 15,
+  12,  9,  6,  3
+};
+
 #define DEFVCORE(r)                                                    \
   static int v_core_##r(dstr *v)                                       \
   {                                                                    \
     salsa20_matrix a, b;                                               \
     dstr d = DSTR_INIT;                                                        \
-    int i, n;                                                          \
+    int i, j, n;                                                       \
     int ok = 1;                                                                \
                                                                        \
     DENSURE(&d, SALSA20_OUTSZ); d.len = SALSA20_OUTSZ;                 \
     n = *(int *)v[0].buf;                                              \
     for (i = 0; i < SALSA20_OUTSZ/4; i++)                              \
-      a[i] = LOAD32_L(v[1].buf + 4*i);                                 \
+      b[i] = LOAD32_L(v[1].buf + 4*i);                                 \
     for (i = 0; i < n; i++) {                                          \
+      for (j = 0; j < 16; j++) a[perm[j]] = b[j];                      \
       core(r, a, b);                                                   \
       memcpy(a, b, sizeof(a));                                         \
     }                                                                  \
-    for (i = 0; i < SALSA20_OUTSZ/4; i++) STORE32_L(d.buf + 4*i, a[i]);        \
+    for (i = 0; i < SALSA20_OUTSZ/4; i++) STORE32_L(d.buf + 4*i, b[i]);        \
                                                                        \
     if (d.len != v[2].len || memcmp(d.buf, v[2].buf, v[2].len) != 0) { \
       ok = 0;                                                          \
index 57cfc10..0ba56c0 100644 (file)
@@ -529,7 +529,7 @@ static const grand_ops grops = {
   "seal",
   GRAND_CRYPTO, 0,
   grmisc, grdestroy,
-  grword, grbyte, grword, grand_range, grfill
+  grword, grbyte, grword, grand_defaultrange, grfill
 };
 
 /* --- @seal_rand@ --- *
diff --git a/vars.am b/vars.am
index b0cd53c..ea5cbc0 100644 (file)
--- a/vars.am
+++ b/vars.am
 ###--------------------------------------------------------------------------
 ### Miscellaneous useful definitions.
 
-## Some convenient abbreviations for file suffixes.
-e                       = $(EXEEXT)
-o                       = $(OBJEXT)
-t                       = t$e
-
 ## Installation directories.
 archincludedir          = $(pkglibdir)/include
 
@@ -95,7 +90,9 @@ SUBST = $(V_SUBST)$(confsubst)
 
 CATACOMB_INCLUDES       = \
        -I$(top_srcdir) \
-       -I$(precomp) \
+       -I$(precomp)/math \
+       -I$(precomp)/misc \
+       -I$(precomp)/symm \
        -I$(top_srcdir)/base \
        -I$(top_srcdir)/key \
        -I$(top_srcdir)/math \
@@ -111,13 +108,13 @@ AM_CPPFLAGS                = $(CATACOMB_INCLUDES)
 ###--------------------------------------------------------------------------
 ### Testing.
 
-SUFFIXES               += .c .$t .to
+SUFFIXES               += .c .t$(EXEEXT) .to
 .c.to:
        $(AM_V_CC)$(COMPILE) -c -DTEST_RIG -DSRCDIR=\"$(srcdir)\" $< -o $@
-.to.$t:
+.to.t$(EXEEXT):
        $(AM_V_CCLD)$(LINK) $< $(TEST_LIBS) $(top_builddir)/libcatacomb.la \
                $(mLib_LIBS) $(CATACOMB_LIBS) $(LIBS)
 .PRECIOUS: %.to
-CLEANFILES             += *.to *.$t
+CLEANFILES             += *.to *.t$(EXEEXT)
 
 ###----- That's all, folks --------------------------------------------------