Update crypto code from Catacomb 2.5.0.
[secnet] / sha3.c
diff --git a/sha3.c b/sha3.c
new file mode 100644 (file)
index 0000000..0fede83
--- /dev/null
+++ b/sha3.c
@@ -0,0 +1,376 @@
+/* -*-c-*-
+ *
+ * The SHA3 algorithm family
+ *
+ * (c) 2017 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version d of the License, or
+ * (at your option) any later version.
+ *
+ * secnet is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ *
+ * This file was originally part of Catacomb, but has been automatically
+ * modified for incorporation into secnet: see `import-catacomb-crypto'
+ * for details.
+ *
+ * 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 <assert.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "sha3.h"
+
+/*----- General utilities -------------------------------------------------*/
+
+static void absorb(sha3_ctx *ctx, const octet *p, unsigned r)
+{
+  kludge64 t[25];
+  unsigned i;
+
+/* memdump("absorb", p, 8*r, 0); */
+  for (i = 0; i < r; i++) { LOAD64_L_(t[i], p); p += 8; }
+  keccak1600_mix(&ctx->s, t, r);
+}
+
+static void step(sha3_ctx *ctx) { keccak1600_p(&ctx->s, &ctx->s, 24); }
+
+static void pad(sha3_ctx *ctx, unsigned lo, unsigned hi)
+{
+  size_t spare = ctx->r - ctx->n;
+
+  if (spare == 1)
+    ctx->buf[ctx->n] = lo | hi;
+  else {
+    ctx->buf[ctx->n] = lo;
+    ctx->buf[ctx->r - 1] = hi;
+    memset(ctx->buf + ctx->n + 1, 0, spare - 2);
+  }
+  absorb(ctx, ctx->buf, ctx->r/8);
+}
+
+static void squeeze(sha3_ctx *ctx, octet *p, unsigned r)
+{
+  kludge64 t[25];
+  unsigned i;
+
+  keccak1600_extract(&ctx->s, t, r);
+  for (i = 0; i < r; i++) { STORE64_L_(p, t[i]); p += 8; }
+/* memdump("squeeze", p - 8*r, 8*r, 0); */
+}
+
+enum {
+  OP_CSHAKE = 0x04,
+  OP_SHA3 = 0x06,
+  OP_SHAKE = 0x1f
+};
+
+enum { ST_ABSORB, ST_SQUEEZE, ST_DEAD };
+
+/*----- The SHA3 algorithms -----------------------------------------------*/
+
+/* --- @sha3_{224,256,384,512}_init@ --- *
+ *
+ * Arguments:  @sha3_ctx *ctx@ = pointer to context block to initialize
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes a SHA3 hashing context for use.
+ */
+
+static void init_sha3(sha3_ctx *ctx, unsigned w)
+{
+  keccak1600_init(&ctx->s);
+  ctx->w = w/8; ctx->r = (1600 - 2*w)/8; ctx->n = 0;
+}
+
+void sha3_224_init(sha3_ctx *ctx) { init_sha3(ctx, 224); }
+void sha3_256_init(sha3_ctx *ctx) { init_sha3(ctx, 256); }
+void sha3_384_init(sha3_ctx *ctx) { init_sha3(ctx, 384); }
+void sha3_512_init(sha3_ctx *ctx) { init_sha3(ctx, 512); }
+
+/* --- @sha3_hash@ --- *
+ *
+ * Arguments:  @sha3_ctx *ctx@ = pointer to context bock
+ *             @const void *p@ = pointer to data to hash
+ *             @size_t sz@ = size of buffer to hash
+ *
+ * Returns:    ---
+ *
+ * Use:                Hashes a buffer of data.  The buffer may be of any size and
+ *             alignment.
+ */
+
+void sha3_hash(sha3_ctx *ctx, const void *p, size_t sz)
+{
+  const octet *q = p;
+  size_t spare = ctx->r - ctx->n;
+
+  if (sz < spare) {
+    memcpy(ctx->buf + ctx->n, q, sz);
+    ctx->n += sz;
+    return;
+  }
+  if (ctx->n) {
+    memcpy(ctx->buf + ctx->n, q, spare);
+    absorb(ctx, ctx->buf, ctx->r/8);
+    step(ctx);
+    q += spare; sz -= spare;
+  }
+  while (sz >= ctx->r) {
+    absorb(ctx, q, ctx->r/8);
+    step(ctx);
+    q += ctx->r; sz -= ctx->r;
+  }
+  if (sz) memcpy(ctx->buf, q, sz);
+  ctx->n = sz;
+}
+
+/* --- @sha3_done@ --- *
+ *
+ * Arguments:  @sha3_ctx *ctx@ = pointer to context block
+ *             @void *hash@ = pointer to output buffer
+ *
+ * Returns:    ---
+ *
+ * Use:                Returns the hash of the data read so far.
+ */
+
+void sha3_done(sha3_ctx *ctx, void *hash)
+{
+  pad(ctx, OP_SHA3, 0x80);
+  step(ctx);
+
+  if (ctx->w%8 == 0)
+    squeeze(ctx, hash, ctx->w/8);
+  else {
+    squeeze(ctx, ctx->buf, (ctx->w + 7)/8);
+    memcpy(hash, ctx->buf, ctx->w);
+  }
+}
+
+/*----- The cSHAKE XOF algorithm ------------------------------------------*/
+
+static void leftenc_sz(shake_ctx *ctx, size_t n)
+{
+  kludge64 t;
+  octet b[9];
+  unsigned i;
+
+  SET64(t, ((n&~(size_t)MASK32) >> 16) >> 16, n&MASK32);
+  STORE64_B_(b + 1, t);
+  for (i = 1; i < 8 && !b[i]; i++);
+  i--; b[i] = 8 - i;
+  shake_hash(ctx, b + i, 9 - i);
+}
+
+static void rightenc_sz(shake_ctx *ctx, size_t n)
+{
+  kludge64 t;
+  octet b[9];
+  unsigned i;
+
+  SET64(t, ((n&~(size_t)MASK32) >> 16) >> 16, n&MASK32);
+  STORE64_B_(b, t);
+  for (i = 0; i < 7 && !b[i]; i++);
+  b[8] = 8 - i;
+  shake_hash(ctx, b + i, 9 - i);
+}
+
+static void stringenc(shake_ctx *ctx, const void *p, size_t sz)
+  { leftenc_sz(ctx, 8*sz); if (sz) shake_hash(ctx, p, sz); }
+
+static void bytepad_before(shake_ctx *ctx)
+  { leftenc_sz(ctx, ctx->h.r); }
+
+static void bytepad_after(shake_ctx *ctx)
+{
+  unsigned pad;
+
+  if (ctx->h.n%8) {
+    pad = 8 - ctx->h.n%8;
+    memset(ctx->h.buf + ctx->h.n, 0, pad);
+    ctx->h.n += pad;
+  }
+  if (ctx->h.n) {
+    absorb(&ctx->h, ctx->h.buf, ctx->h.n/8);
+    step(&ctx->h); ctx->h.n = 0;
+  }
+}
+
+/* --- @cshake{128,256}_init@ --- *
+ *
+ * Arguments:  @shake_ctx *ctx@ = pointer to context to initialize
+ *             @const void *func@ = NIST-allocated function name
+ *             @size_t fsz@ = length of function name
+ *             @const void *perso@ = user personalization string
+ *             @size_t psz@ = length of personalization string
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes a cSHAKE context.  The context is initially in
+ *             the `absorbing' state: feed it data with @shake_hash@.
+ */
+
+static void init_shake(shake_ctx *ctx, unsigned c0,
+                      const void *func, size_t fsz,
+                      const void *perso, size_t psz)
+{
+  keccak1600_init(&ctx->h.s); ctx->st = ST_ABSORB;
+  ctx->h.r = (1600 - 2*c0)/8; ctx->h.w = 0; ctx->h.n = 0;
+  if (!fsz && !psz)
+    ctx->op = OP_SHAKE;
+  else {
+    bytepad_before(ctx);
+    stringenc(ctx, func, fsz);
+    stringenc(ctx, perso, psz);
+    bytepad_after(ctx);
+    ctx->op = OP_CSHAKE;
+  }
+}
+
+void cshake128_init(shake_ctx *ctx,
+                   const void *func, size_t fsz,
+                   const void *perso, size_t psz)
+  { init_shake(ctx, 128, func, fsz, perso, psz); }
+
+void cshake256_init(shake_ctx *ctx,
+                   const void *func, size_t fsz,
+                   const void *perso, size_t psz)
+  { init_shake(ctx, 256, func, fsz, perso, psz); }
+
+/* --- @shake{128,256}_init@ --- *
+ *
+ * Arguments:  @sha3_ctx *ctx@ = pointer to context to initialize
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes a SHAKE context.  The context is initially in
+ *             the `absorbing' state: feed it data with @shake_hash@.
+ */
+
+void shake128_init(shake_ctx *ctx) { init_shake(ctx, 128, 0, 0, 0, 0); }
+void shake256_init(shake_ctx *ctx) { init_shake(ctx, 256, 0, 0, 0, 0); }
+
+/* --- @shake_hash@ --- *
+ *
+ * Arguments:  @shake_ctx *ctx@ = context to update
+ *             @const void *p@ = input buffer
+ *             @size_t sz@ = size of input
+ *
+ * Returns:    ---
+ *
+ * Use:                Feeds input data into a SHAKE context.  The context must be
+ *             in `absorbing' state.
+ */
+
+void shake_hash(shake_ctx *ctx, const void *p, size_t sz)
+  { assert(ctx->st == ST_ABSORB); sha3_hash(&ctx->h, p, sz); }
+
+/* --- @shake_xof@ --- *
+ *
+ * Arguments:  @shake_ctx *ctx@ = context to update
+ *
+ * Returns:    ---
+ *
+ * Use:                Switches the context into `squeezing' state.  Use @shake_get@
+ *             or @shake_mask@ to extract data.
+ */
+
+void shake_xof(shake_ctx *ctx)
+{
+  assert(ctx->st == ST_ABSORB);
+  pad(&ctx->h, ctx->op, 0x80);
+  ctx->st = ST_SQUEEZE;
+  ctx->h.n = ctx->h.r;
+}
+
+/* --- @shake_get@ --- *
+ *
+ * Arguments:  @shake_ctx *ctx@ = context to update
+ *             @void *p@ = output buffer
+ *             @size_t sz@ = size of output
+ *
+ * Returns:    ---
+ *
+ * Use:                Extracts output from a SHAKE context.  The context must be
+ *             in `squeezing' state.
+ */
+
+void shake_get(shake_ctx *ctx, void *p, size_t sz)
+{
+  octet *q = p;
+  size_t left = ctx->h.r - ctx->h.n;
+
+  assert(ctx->st == ST_SQUEEZE);
+  if (left >= sz) {
+    memcpy(q, ctx->h.buf + ctx->h.n, sz);
+    ctx->h.n += sz;
+    return;
+  }
+  if (left) {
+    memcpy(q, ctx->h.buf + ctx->h.n, left);
+    q += left; sz -= left;
+  }
+  while (sz >= ctx->h.r) {
+    step(&ctx->h);
+    squeeze(&ctx->h, q, ctx->h.r/8);
+    q += ctx->h.r; sz -= ctx->h.r;
+  }
+  if (!sz)
+    ctx->h.n = ctx->h.r;
+  else {
+    step(&ctx->h);
+    squeeze(&ctx->h, ctx->h.buf, ctx->h.r/8);
+    memcpy(q, ctx->h.buf, sz);
+    ctx->h.n = sz;
+  }
+}
+
+/* --- @shake_done@ --- *
+ *
+ * Arguments:  @shake_ctx *ctx@ = context to update
+ *             @void *h@ = where to write the hash
+ *             @size_t hsz@ = size of the hash to make
+ *
+ * Returns:    ---
+ *
+ * Use:                Switches the context into `squeezing' state.  Use @shake_get@
+ *             or @shake_mask@ to extract data.
+ */
+
+void shake_done(shake_ctx *ctx, void *h, size_t hsz)
+  { shake_xof(ctx); shake_get(ctx, h, hsz); ctx->st = ST_DEAD; }
+
+/*----- That's all, folks -------------------------------------------------*/