@@@ fltfmt mess
[mLib] / hash / siphash.h
diff --git a/hash/siphash.h b/hash/siphash.h
new file mode 100644 (file)
index 0000000..f61714f
--- /dev/null
@@ -0,0 +1,271 @@
+/* -*-c-*-
+ *
+ * The SipHash-2-4 message authentication code
+ *
+ * (c) 2024 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib 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.
+ *
+ * mLib 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 mLib.  If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef MLIB_SIPHASH_H
+#define MLIB_SIPHASH_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <string.h>
+
+#ifndef MLIB_BITS_H
+#  include "bits.h"
+#endif
+
+/*----- Constants and data structures -------------------------------------*/
+
+#define SIPHASH_KEYSZ 16               /* size of a SipHash key in bytes */
+#define SIPHASH_BLKSZ 8                        /* size of SipHash blocks */
+
+struct siphash_key { kludge64 k0, k1; }; /* a prepared SipHash key */
+
+struct siphash {                       /* a SipHash context */
+  kludge64 a, b, c, d;
+  octet buf[SIPHASH_BLKSZ]; unsigned n, msz;
+};
+
+/*----- Implementation macros ---------------------------------------------*/
+
+/* The initial state, not intended for user consumption. */
+#define SIPHASH__A0 X64(736f6d65, 70736575) /* some pseu */
+#define SIPHASH__B0 X64(646f7261, 6e646f6d) /* dora ndom */
+#define SIPHASH__C0 X64(6c796765, 6e657261) /* lyge nera */
+#define SIPHASH__D0 X64(74656462, 79746573) /* tedb ytes */
+
+/* --- @SIPHASH__ROUND@ --- *
+ *
+ * Arguments:  @kludge64 &a, &b, &c, &d@ = four state variables (updated)
+ *
+ * Returns:    ---
+ *
+ * Use:                Does one round of SipHash.  Not really intended for user
+ *             consumption.
+ */
+
+#define SIPHASH__ROUND(a, b, c, d) do {                                        \
+  ADD64((a), (a), (b));        ADD64((c), (c), (d));                           \
+  ROL64_((b), (b), 13);        ROL64_((d), (d), 16);                           \
+  XOR64((b), (b), (a));        XOR64((d), (d), (c));                           \
+  ROL64_((a), (a), 32);                                                        \
+  ADD64((c), (c), (b));        ADD64((a), (a), (d));                           \
+  ROL64_((b), (b), 17);        ROL64_((d), (d), 21);                           \
+  XOR64((b), (b), (c));        XOR64((d), (d), (a));                           \
+  ROL64_((c), (c), 32);                                                        \
+} while (0)
+
+/* --- @SIPHASH_INIT@ --- *
+ *
+ * Arguments:  @kludge64 &a, &b, &c, &d@ = four state variables (output)
+ *             @const struct siphash_key *k@ = prepared key
+ *
+ * Returns:    ---
+ *
+ * Use:                Initialize a SipHash state.
+ */
+
+#define SIPHASH_INIT(a, b, c, d, k) do {                               \
+  kludge64                                                             \
+    _a0 = SIPHASH__A0, _b0 = SIPHASH__B0,                              \
+    _c0 = SIPHASH__C0, _d0 = SIPHASH__D0;                              \
+  const struct siphash_key *_k = (k);                                  \
+                                                                       \
+  XOR64((a), _a0, _k->k0); XOR64((b), _b0, _k->k1);                    \
+  XOR64((c), _c0, _k->k0); XOR64((d), _d0, _k->k1);                    \
+} while (0)
+
+/* --- @SIPHASH_WORD@ --- *
+ *
+ * Arguments:  @kludge64 &a, &b, &c, &d@ = four state variables (updated)
+ *             @kludge64 m@ = message word (multiply evaluated)
+ *
+ * Returns:    ---
+ *
+ * Use:                Update a SipHash state with a single message word.
+ */
+
+#define SIPHASH_WORD(a, b, c, d, m) do {                               \
+  XOR64((d), (d), (m));                                                        \
+  SIPHASH__ROUND(a, b, c, d); SIPHASH__ROUND(a, b, c, d);              \
+  XOR64((a), (a), (m));                                                        \
+} while (0)
+
+/* --- @SIPHASH_FINAL@ --- *
+ *
+ * Arguments:  @kludge64 &a, &b, &c, &d@ = four state variables (updated)
+ *             @kludge64 &z_out@ = hash result (output)
+ *             @const void *p@ = pointer to message tail
+ *             @unsigned n@ = length of message tail in bytes (must be less
+ *                     than @SIPHASH_BLKSZ@)
+ *             @size_t msz@ = overall message size (at least mod 256)
+ *
+ * Returns:    ---
+ *
+ * Use:                Completes a SipHash operation, storing the final result in
+ *             @z@.
+ */
+
+#define SIPHASH_FINAL(a, b, c, d, z_out, p, n, msz) do {               \
+  const unsigned char *_p = (const unsigned char *)(p);                        \
+  kludge64 _t, _u;                                                     \
+                                                                       \
+  /* Prepare the final message word.                                   \
+   *                                                                   \
+   * This is kind of annoying.  Overrunning the input buffer could be   \
+   * disastrous.  This fiddly switch seems faster than building the    \
+   * value in a byte buffer and then loading it.                       \
+   */                                                                  \
+  SET64(_t, 0, 0); SETBYTE64(_t, (msz), 7);                            \
+  switch (n) {                                                         \
+    /* case 7: */                                                      \
+    default: SETBYTE64(_t, _p[6], 6);                                  \
+    case 6:  SETBYTE64(_t, _p[5], 5);                                  \
+    case 5:  SETBYTE64(_t, _p[4], 4);                                  \
+    case 4:  SETBYTE64(_t, _p[3], 3);                                  \
+    case 3:  SETBYTE64(_t, _p[2], 2);                                  \
+    case 2:  SETBYTE64(_t, _p[1], 1);                                  \
+    case 1:  SETBYTE64(_t, _p[0], 0);                                  \
+    case 0:  break;                                                    \
+  }                                                                    \
+                                                                       \
+  /* Finish the hashing job. */                                                \
+  SIPHASH_WORD(a, b, c, d, _t);                                                \
+  SET64(_t, 0, 0xff); XOR64((c), (c), _t);                             \
+  SIPHASH__ROUND(a, b, c, d); SIPHASH__ROUND(a, b, c, d);              \
+  SIPHASH__ROUND(a, b, c, d); SIPHASH__ROUND(a, b, c, d);              \
+                                                                       \
+  /* Combine the hash-state words into a single result. */             \
+  XOR64(_t, (a), (b)); XOR64(_u, (c), (d)); XOR64((z_out), _t, _u);    \
+  SET64((a), 0, 0); SET64((b), 0, 0); SET64((c), 0, 0); SET64((d), 0, 0); \
+} while (0)
+
+/* --- @SIPHASH@ --- *
+ *
+ * Arguments:  @const struct siphash_key *k@ = prepared key
+ *             @kludge64 &z_out@ = hash result (output)
+ *             @const void *p@ = pointer to message
+ *             @size_t sz@ = message size in bytes
+ *
+ * Returns:    ---
+ *
+ * Use:                Hash a message using under the key @k@, leaving the result in
+ *             @z@.
+ */
+
+#define SIPHASH(k, z_out, p, sz) do {                                  \
+  kludge64 _a, _b, _c, _d, _m;                                         \
+  const octet *_q = (p); size_t _sz0 = (sz), _sz = _sz0;               \
+                                                                       \
+  SIPHASH_INIT(_a, _b, _c, _d, (k));                                   \
+  while (_sz >= SIPHASH_BLKSZ) {                                       \
+    LOAD64_L_(_m, _q); SIPHASH_WORD(_a, _b, _c, _d, _m);               \
+    _q += SIPHASH_BLKSZ; _sz -= SIPHASH_BLKSZ;                         \
+  }                                                                    \
+  SIPHASH_FINAL(_a, _b, _c, _d, (z_out), _q, _sz, _sz0);               \
+} while (0)
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @siphash_setkey@ --- *
+ *
+ * Arguments:  @struct siphash_key *k@ = prepared key structure to fill in
+ *             @const octet *p@ = pointer to key data (@SIPHASH_KEYSZ = 16@
+ *                     bytes)
+ *
+ * Returns:    ---
+ *
+ * Use:                Prepare a SipHash key.
+ */
+
+extern void siphash_setkey(struct siphash_key */*k*/, const octet */*p*/);
+
+/* --- @siphash_init@ --- *
+ *
+ * Arguments:  @struct siphash *s@ = hashing state to initialize
+ *             @const struct siphash_key *k@ = prepared key structure
+ *
+ * Returns:    ---
+ *
+ * Use:                Initialize a state for hashing a message in multiple pieces.
+ */
+
+extern void siphash_init(struct siphash */*s*/,
+                        const struct siphash_key */*k*/);
+
+/* --- @siphash_hash@ --- *
+ *
+ * Arguments:  @struct siphash *s@ = hashing state
+ *             @const void *p, size_t sz@ = input message buffer
+ *
+ * Returns:    ---
+ *
+ * Use:                Update the hashing state by processing the provided input
+ *             message chunk.  The address and size do not need to be
+ *             aligned in any particular way.
+ */
+
+extern void siphash_hash(struct siphash */*s*/,
+                        const void */*p*/, size_t /*sz*/);
+
+/* --- @siphash_done@ --- *
+ *
+ * Arguments:  @struct siphash *s@ = hashing state (clobbered)
+ *
+ * Returns:    The completed hash.
+ *
+ * Use:                Complete the hashing operation tracked by the state,
+ *             returning the resulting 64-bit hash.
+ */
+
+extern kludge64 siphash_done(struct siphash */*s*/);
+
+/* --- @siphash@ --- *
+ *
+ * Arguments:  @const struct siphash_key *k@ = prepared key
+ *             @const void *p, size_t sz@ = input message buffer
+ *
+ * Returns:    The completed hash.
+ *
+ * Use:                Hash the message data in a single input buffer under the
+ *             control of the supplied key, returning the resulting hash.
+ *             This is simpler and faster than the @init@/@hash@/@done@
+ *             interface.
+ */
+
+extern kludge64 siphash(const struct siphash_key */*k*/,
+                       const void */*p*/, size_t /*sz*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif