3 * The SipHash message authentication code
5 * (c) 2024 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
28 #ifndef MLIB_SIPHASH_H
29 #define MLIB_SIPHASH_H
35 /*----- Header files ------------------------------------------------------*/
43 /*----- Constants and data structures -------------------------------------*/
45 #define SIPHASH_KEYSZ 16 /* size of a SipHash key in bytes */
46 #define SIPHASH_BLKSZ 8 /* size of SipHash blocks */
48 struct siphash_key { kludge64 k0, k1; }; /* a prepared SipHash key */
50 #define SIPHASH_STRUCT { /* a SipHash context */ \
51 kludge64 a, b, c, d; \
52 octet buf[SIPHASH_BLKSZ]; unsigned n, msz; \
55 /*----- Implementation macros ---------------------------------------------*/
57 /* The initial state, not intended for user consumption. */
58 #define SIPHASH__A0 X64(736f6d65, 70736575) /* some pseu */
59 #define SIPHASH__B0 X64(646f7261, 6e646f6d) /* dora ndom */
60 #define SIPHASH__C0 X64(6c796765, 6e657261) /* lyge nera */
61 #define SIPHASH__D0 X64(74656462, 79746573) /* tedb ytes */
63 /* --- @SIPHASH__ROUND@ --- *
65 * Arguments: @kludge64 &a, &b, &c, &d@ = four state variables (updated)
69 * Use: Does one round of SipHash. Not really intended for user
73 #define SIPHASH__ROUND(a, b, c, d) \
74 ADD64((a), (a), (b)); ADD64((c), (c), (d)); \
75 ROL64_((b), (b), 13); ROL64_((d), (d), 16); \
76 XOR64((b), (b), (a)); XOR64((d), (d), (c)); \
77 ROL64_((a), (a), 32); \
78 ADD64((c), (c), (b)); ADD64((a), (a), (d)); \
79 ROL64_((b), (b), 17); ROL64_((d), (d), 21); \
80 XOR64((b), (b), (c)); XOR64((d), (d), (a)); \
83 /* --- @SIPHASH__NROUNDS@ --- *
85 * Arguments: @unsigned n@ = (decimal constant) number of rounds
86 * @kludge64 &a, &b, &c, &d@ = four state variables (updated)
90 * Use: Does @n@ rounds of SipHash. Not really intended for user
94 #define SIPHASH__NROUNDS_0(a, b, c, d) \
96 #define SIPHASH__NROUNDS_1(a, b, c, d) \
97 SIPHASH__ROUND(a, b, c, d)
98 #define SIPHASH__NROUNDS_2(a, b, c, d) \
99 SIPHASH__NROUNDS_1(a, b, c, d); SIPHASH__ROUND(a, b, c, d)
100 #define SIPHASH__NROUNDS_3(a, b, c, d) \
101 SIPHASH__NROUNDS_2(a, b, c, d); SIPHASH__ROUND(a, b, c, d)
102 #define SIPHASH__NROUNDS_4(a, b, c, d) \
103 SIPHASH__NROUNDS_3(a, b, c, d); SIPHASH__ROUND(a, b, c, d)
104 #define SIPHASH__NROUNDS(n, a, b, c, d) \
105 do { SIPHASH__NROUNDS_##n(a, b, c, d); } while (0)
107 /* --- @SIPHASH_INIT@ --- *
109 * Arguments: @kludge64 &a, &b, &c, &d@ = four state variables (output)
110 * @const struct siphash_key *k@ = prepared key
114 * Use: Initialize a SipHash state.
117 #define SIPHASH_INIT(a, b, c, d, k) do { \
119 _a0 = SIPHASH__A0, _b0 = SIPHASH__B0, \
120 _c0 = SIPHASH__C0, _d0 = SIPHASH__D0; \
121 const struct siphash_key *_k = (k); \
123 XOR64((a), _a0, _k->k0); XOR64((b), _b0, _k->k1); \
124 XOR64((c), _c0, _k->k0); XOR64((d), _d0, _k->k1); \
127 /* --- @SIPHASH_WORD@ --- *
129 * Arguments: @unsigned nr0@ = number of interstitial rounds
130 * @kludge64 &a, &b, &c, &d@ = four state variables (updated)
131 * @kludge64 m@ = message word (multiply evaluated)
135 * Use: Update a SipHash state with a single message word.
138 #define SIPHASH_WORD(nr0, a, b, c, d, m) do { \
139 XOR64((d), (d), (m)); \
140 SIPHASH__NROUNDS(nr0, a, b, c, d); \
141 XOR64((a), (a), (m)); \
144 /* --- @SIPHASH_FINAL@ --- *
146 * Arguments: @unsigned nr0, nr1@ = number of interstitial and final rounds
147 * @kludge64 &a, &b, &c, &d@ = four state variables (updated)
148 * @kludge64 &z_out@ = hash result (output)
149 * @const void *p@ = pointer to message tail
150 * @unsigned n@ = length of message tail in bytes (must be less
151 * than @SIPHASH_BLKSZ@)
152 * @size_t msz@ = overall message size (at least mod 256)
156 * Use: Completes a SipHash operation, storing the final result in
160 #define SIPHASH_FINAL(nr0, nr1, a, b, c, d, z_out, p, n, msz) do { \
161 const unsigned char *_p = (const unsigned char *)(p); \
164 /* Prepare the final message word. \
166 * This is kind of annoying. Overrunning the input buffer could be \
167 * disastrous. This fiddly switch seems faster than building the \
168 * value in a byte buffer and then loading it. \
170 SET64(_t, 0, 0); SETBYTE64(_t, (msz), 7); \
173 default: SETBYTE64(_t, _p[6], 6); \
174 case 6: SETBYTE64(_t, _p[5], 5); \
175 case 5: SETBYTE64(_t, _p[4], 4); \
176 case 4: SETBYTE64(_t, _p[3], 3); \
177 case 3: SETBYTE64(_t, _p[2], 2); \
178 case 2: SETBYTE64(_t, _p[1], 1); \
179 case 1: SETBYTE64(_t, _p[0], 0); \
183 /* Finish the hashing job. */ \
184 SIPHASH_WORD(nr0, a, b, c, d, _t); \
185 SET64(_t, 0, 0xff); XOR64((c), (c), _t); \
186 SIPHASH__NROUNDS(nr1, a, b, c, d); \
188 /* Combine the hash-state words into a single result. */ \
189 XOR64(_t, (a), (b)); XOR64(_u, (c), (d)); XOR64((z_out), _t, _u); \
190 SET64((a), 0, 0); SET64((b), 0, 0); SET64((c), 0, 0); SET64((d), 0, 0); \
193 /* --- @SIPHASH@, @SIPHASH13@, @SIPHASH24@ --- *
195 * Arguments: @unsigned nr0, nr1@ = number of interstitial and final rounds
196 * @const struct siphash_key *k@ = prepared key
197 * @kludge64 &z_out@ = hash result (output)
198 * @const void *p@ = pointer to message
199 * @size_t sz@ = message size in bytes
203 * Use: Hash a message using under the key @k@, leaving the result in
207 #define SIPHASH(nr0, nr1, k, z_out, p, sz) do { \
208 kludge64 _a, _b, _c, _d, _m; \
209 const octet *_q = (p); size_t _sz0 = (sz), _sz = _sz0; \
211 SIPHASH_INIT(_a, _b, _c, _d, (k)); \
212 while (_sz >= SIPHASH_BLKSZ) { \
213 LOAD64_L_(_m, _q); SIPHASH_WORD(nr0, _a, _b, _c, _d, _m); \
214 _q += SIPHASH_BLKSZ; _sz -= SIPHASH_BLKSZ; \
216 SIPHASH_FINAL(nr0, nr1, _a, _b, _c, _d, (z_out), _q, _sz, _sz0); \
219 /*----- Functions provided ------------------------------------------------*/
221 #define SIPHASH_VARIANTS(_) _(1, 3) _(2, 4)
223 #define SIPHASH_DEFSTRUCT(nr0, nr1) \
224 struct siphash##nr0##nr1 SIPHASH_STRUCT;
225 SIPHASH_VARIANTS(SIPHASH_DEFSTRUCT)
227 /* --- @siphash_setkey@ --- *
229 * Arguments: @struct siphash_key *k@ = prepared key structure to fill in
230 * @const octet *p@ = pointer to key data (@SIPHASH_KEYSZ = 16@
235 * Use: Prepare a SipHash key.
238 extern void siphash_setkey(struct siphash_key */*k*/, const octet */*p*/);
240 /* --- @siphash_init@ --- *
242 * Arguments: @struct siphash *s@ = hashing state to initialize
243 * @const struct siphash_key *k@ = prepared key structure
247 * Use: Initialize a state for hashing a message in multiple pieces.
250 #define SIPHASH_DECLINIT(nr0, nr1) \
251 extern void siphash##nr0##nr1##_init(struct siphash##nr0##nr1 */*s*/, \
252 const struct siphash_key */*k*/);
254 #define SIPHASH_DEFINIT(nr0, nr1) \
255 void siphash##nr0##nr1##_init(struct siphash##nr0##nr1 *s, \
256 const struct siphash_key *k) \
257 { SIPHASH_INIT(s->a, s->b, s->c, s->d, k); s->n = s->msz = 0; }
259 SIPHASH_VARIANTS(SIPHASH_DECLINIT)
261 /* --- @siphash13_hash@, @siphash24_hash@ --- *
263 * Arguments: @struct siphash *s@ = hashing state
264 * @const void *p, size_t sz@ = input message buffer
268 * Use: Update the hashing state by processing the provided input
269 * message chunk. The address and size do not need to be
270 * aligned in any particular way.
273 #define SIPHASH_DECLHASH(nr0, nr1) \
274 extern void siphash##nr0##nr1##_hash(struct siphash##nr0##nr1 */*s*/, \
275 const void */*p*/, size_t /*sz*/);
277 #define SIPHASH_DEFHASH(nr0, nr1) \
278 void siphash##nr0##nr1##_hash(struct siphash##nr0##nr1 *s, \
279 const void *p, size_t sz) \
281 const octet *q = p; \
286 n = SIPHASH_BLKSZ - s->n; \
288 { memcpy(s->buf + s->n, q, sz); s->n += sz; return; } \
291 memcpy(s->buf + s->n, q, n); q += n; sz -= n; \
292 LOAD64_L_(m, s->buf); SIPHASH_WORD(nr0, s->a, s->b, s->c, s->d, m); \
294 while (sz >= SIPHASH_BLKSZ) { \
295 LOAD64_L_(m, q); SIPHASH_WORD(nr0, s->a, s->b, s->c, s->d, m); \
296 q += SIPHASH_BLKSZ; sz -= SIPHASH_BLKSZ; \
298 if (sz) memcpy(s->buf, q, sz); \
302 SIPHASH_VARIANTS(SIPHASH_DECLHASH)
304 /* --- @siphash13_done@, @siphash24_done@ --- *
306 * Arguments: @struct siphash *s@ = hashing state (clobbered)
308 * Returns: The completed hash.
310 * Use: Complete the hashing operation tracked by the state,
311 * returning the resulting 64-bit hash.
314 #define SIPHASH_DECLDONE(nr0, nr1) \
315 extern kludge64 siphash##nr0##nr1##_done(struct siphash##nr0##nr1 */*s*/);
317 #define SIPHASH_DEFDONE(nr0, nr1) \
318 kludge64 siphash##nr0##nr1##_done(struct siphash##nr0##nr1 *s) \
322 SIPHASH_FINAL(nr0, nr1, s->a, s->b, s->c, s->d, \
323 z, s->buf, s->n, s->msz); \
327 SIPHASH_VARIANTS(SIPHASH_DECLDONE)
329 /* --- @siphash13@, @siphash24@ --- *
331 * Arguments: @const struct siphash_key *k@ = prepared key
332 * @const void *p, size_t sz@ = input message buffer
334 * Returns: The completed hash.
336 * Use: Hash the message data in a single input buffer under the
337 * control of the supplied key, returning the resulting hash.
338 * This is simpler and faster than the @init@/@hash@/@done@
342 #define SIPHASH_DECLALLINONE(nr0, nr1) \
343 extern kludge64 siphash##nr0##nr1(const struct siphash_key */*k*/, \
344 const void */*p*/, size_t /*sz*/);
346 #define SIPHASH_DEFALLINONE(nr0, nr1) \
347 kludge64 siphash##nr0##nr1(const struct siphash_key *k, \
348 const void *p, size_t sz) \
349 { kludge64 z; SIPHASH(nr0, nr1, k, z, p, sz); return (z); }
351 SIPHASH_VARIANTS(SIPHASH_DECLALLINONE)
353 /*----- That's all, folks -------------------------------------------------*/