--- /dev/null
+/* -*-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