chiark / gitweb /
@@@ fltfmt fettling
[mLib] / hash / siphash.h
1 /* -*-c-*-
2  *
3  * The SipHash message authentication code
4  *
5  * (c) 2024 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the mLib utilities library.
11  *
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.
16  *
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.
21  *
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,
25  * USA.
26  */
27
28 #ifndef MLIB_SIPHASH_H
29 #define MLIB_SIPHASH_H
30
31 #ifdef __cplusplus
32   extern "C" {
33 #endif
34
35 /*----- Header files ------------------------------------------------------*/
36
37 #include <string.h>
38
39 #ifndef MLIB_BITS_H
40 #  include "bits.h"
41 #endif
42
43 /*----- Constants and data structures -------------------------------------*/
44
45 #define SIPHASH_KEYSZ 16                /* size of a SipHash key in bytes */
46 #define SIPHASH_BLKSZ 8                 /* size of SipHash blocks */
47
48 struct siphash_key { kludge64 k0, k1; }; /* a prepared SipHash key */
49
50 #define SIPHASH_STRUCT {                /* a SipHash context */         \
51   kludge64 a, b, c, d;                                                  \
52   octet buf[SIPHASH_BLKSZ]; unsigned n, msz;                            \
53 }
54
55 /*----- Implementation macros ---------------------------------------------*/
56
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 */
62
63 /* --- @SIPHASH__ROUND@ --- *
64  *
65  * Arguments:   @kludge64 &a, &b, &c, &d@ = four state variables (updated)
66  *
67  * Returns:     ---
68  *
69  * Use:         Does one round of SipHash.  Not really intended for user
70  *              consumption.
71  */
72
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));                           \
81   ROL64_((c), (c), 32)
82
83 /* --- @SIPHASH__NROUNDS@ --- *
84  *
85  * Arguments:   @unsigned n@ = (decimal constant) number of rounds
86  *              @kludge64 &a, &b, &c, &d@ = four state variables (updated)
87  *
88  * Returns:     ---
89  *
90  * Use:         Does @n@ rounds of SipHash.  Not really intended for user
91  *              consumption.
92  */
93
94 #define SIPHASH__NROUNDS_0(a, b, c, d)                                  \
95   ;
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)
106
107 /* --- @SIPHASH_INIT@ --- *
108  *
109  * Arguments:   @kludge64 &a, &b, &c, &d@ = four state variables (output)
110  *              @const struct siphash_key *k@ = prepared key
111  *
112  * Returns:     ---
113  *
114  * Use:         Initialize a SipHash state.
115  */
116
117 #define SIPHASH_INIT(a, b, c, d, k) do {                                \
118   kludge64                                                              \
119     _a0 = SIPHASH__A0, _b0 = SIPHASH__B0,                               \
120     _c0 = SIPHASH__C0, _d0 = SIPHASH__D0;                               \
121   const struct siphash_key *_k = (k);                                   \
122                                                                         \
123   XOR64((a), _a0, _k->k0); XOR64((b), _b0, _k->k1);                     \
124   XOR64((c), _c0, _k->k0); XOR64((d), _d0, _k->k1);                     \
125 } while (0)
126
127 /* --- @SIPHASH_WORD@ --- *
128  *
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)
132  *
133  * Returns:     ---
134  *
135  * Use:         Update a SipHash state with a single message word.
136  */
137
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));                                                 \
142 } while (0)
143
144 /* --- @SIPHASH_FINAL@ --- *
145  *
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)
153  *
154  * Returns:     ---
155  *
156  * Use:         Completes a SipHash operation, storing the final result in
157  *              @z@.
158  */
159
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);                 \
162   kludge64 _t, _u;                                                      \
163                                                                         \
164   /* Prepare the final message word.                                    \
165    *                                                                    \
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.                        \
169    */                                                                   \
170   SET64(_t, 0, 0); SETBYTE64(_t, (msz), 7);                             \
171   switch (n) {                                                          \
172     /* case 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);                                   \
180     case 0:  break;                                                     \
181   }                                                                     \
182                                                                         \
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);                                    \
187                                                                         \
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); \
191 } while (0)
192
193 /* --- @SIPHASH@, @SIPHASH13@, @SIPHASH24@ --- *
194  *
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
200  *
201  * Returns:     ---
202  *
203  * Use:         Hash a message using under the key @k@, leaving the result in
204  *              @z@.
205  */
206
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;                \
210                                                                         \
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;                          \
215   }                                                                     \
216   SIPHASH_FINAL(nr0, nr1, _a, _b, _c, _d, (z_out), _q, _sz, _sz0);      \
217 } while (0)
218
219 /*----- Functions provided ------------------------------------------------*/
220
221 #define SIPHASH_VARIANTS(_) _(1, 3) _(2, 4)
222
223 #define SIPHASH_DEFSTRUCT(nr0, nr1)                                     \
224   struct siphash##nr0##nr1 SIPHASH_STRUCT;
225 SIPHASH_VARIANTS(SIPHASH_DEFSTRUCT)
226
227 /* --- @siphash_setkey@ --- *
228  *
229  * Arguments:   @struct siphash_key *k@ = prepared key structure to fill in
230  *              @const octet *p@ = pointer to key data (@SIPHASH_KEYSZ = 16@
231  *                      bytes)
232  *
233  * Returns:     ---
234  *
235  * Use:         Prepare a SipHash key.
236  */
237
238 extern void siphash_setkey(struct siphash_key */*k*/, const octet */*p*/);
239
240 /* --- @siphash_init@ --- *
241  *
242  * Arguments:   @struct siphash *s@ = hashing state to initialize
243  *              @const struct siphash_key *k@ = prepared key structure
244  *
245  * Returns:     ---
246  *
247  * Use:         Initialize a state for hashing a message in multiple pieces.
248  */
249
250 #define SIPHASH_DECLINIT(nr0, nr1)                                      \
251   extern void siphash##nr0##nr1##_init(struct siphash##nr0##nr1 */*s*/, \
252                                        const struct siphash_key */*k*/);
253
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; }
258
259 SIPHASH_VARIANTS(SIPHASH_DECLINIT)
260
261 /* --- @siphash13_hash@, @siphash24_hash@ --- *
262  *
263  * Arguments:   @struct siphash *s@ = hashing state
264  *              @const void *p, size_t sz@ = input message buffer
265  *
266  * Returns:     ---
267  *
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.
271  */
272
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*/);
276
277 #define SIPHASH_DEFHASH(nr0, nr1)                                       \
278   void siphash##nr0##nr1##_hash(struct siphash##nr0##nr1 *s,            \
279                                 const void *p, size_t sz)               \
280   {                                                                     \
281     const octet *q = p;                                                 \
282     kludge64 m;                                                         \
283     size_t n;                                                           \
284                                                                         \
285     s->msz += sz;                                                       \
286     n = SIPHASH_BLKSZ - s->n;                                           \
287     if (sz < n)                                                         \
288       { memcpy(s->buf + s->n, q, sz); s->n += sz; return; }             \
289                                                                         \
290     if (s->n) {                                                         \
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); \
293     }                                                                   \
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;                          \
297     }                                                                   \
298     if (sz) memcpy(s->buf, q, sz);                                      \
299     s->n = sz;                                                          \
300   }
301
302 SIPHASH_VARIANTS(SIPHASH_DECLHASH)
303
304 /* --- @siphash13_done@, @siphash24_done@ --- *
305  *
306  * Arguments:   @struct siphash *s@ = hashing state (clobbered)
307  *
308  * Returns:     The completed hash.
309  *
310  * Use:         Complete the hashing operation tracked by the state,
311  *              returning the resulting 64-bit hash.
312  */
313
314 #define SIPHASH_DECLDONE(nr0, nr1)                                      \
315   extern kludge64 siphash##nr0##nr1##_done(struct siphash##nr0##nr1 */*s*/);
316
317 #define SIPHASH_DEFDONE(nr0, nr1)                                       \
318   kludge64 siphash##nr0##nr1##_done(struct siphash##nr0##nr1 *s)        \
319   {                                                                     \
320     kludge64 z;                                                         \
321                                                                         \
322     SIPHASH_FINAL(nr0, nr1, s->a, s->b, s->c, s->d,                     \
323                   z, s->buf, s->n, s->msz);                             \
324     return (z);                                                         \
325   }
326
327 SIPHASH_VARIANTS(SIPHASH_DECLDONE)
328
329 /* --- @siphash13@, @siphash24@ --- *
330  *
331  * Arguments:   @const struct siphash_key *k@ = prepared key
332  *              @const void *p, size_t sz@ = input message buffer
333  *
334  * Returns:     The completed hash.
335  *
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@
339  *              interface.
340  */
341
342 #define SIPHASH_DECLALLINONE(nr0, nr1)                                  \
343   extern kludge64 siphash##nr0##nr1(const struct siphash_key */*k*/,    \
344                                     const void */*p*/, size_t /*sz*/);
345
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); }
350
351 SIPHASH_VARIANTS(SIPHASH_DECLALLINONE)
352
353 /*----- That's all, folks -------------------------------------------------*/
354
355 #ifdef __cplusplus
356   }
357 #endif
358
359 #endif