chiark / gitweb /
f61714fd1331947f7df8e57aee1c9fd333fe5b74
[mLib] / hash / siphash.h
1 /* -*-c-*-
2  *
3  * The SipHash-2-4 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 struct siphash {                        /* 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) do {                                 \
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 } while (0)
83
84 /* --- @SIPHASH_INIT@ --- *
85  *
86  * Arguments:   @kludge64 &a, &b, &c, &d@ = four state variables (output)
87  *              @const struct siphash_key *k@ = prepared key
88  *
89  * Returns:     ---
90  *
91  * Use:         Initialize a SipHash state.
92  */
93
94 #define SIPHASH_INIT(a, b, c, d, k) do {                                \
95   kludge64                                                              \
96     _a0 = SIPHASH__A0, _b0 = SIPHASH__B0,                               \
97     _c0 = SIPHASH__C0, _d0 = SIPHASH__D0;                               \
98   const struct siphash_key *_k = (k);                                   \
99                                                                         \
100   XOR64((a), _a0, _k->k0); XOR64((b), _b0, _k->k1);                     \
101   XOR64((c), _c0, _k->k0); XOR64((d), _d0, _k->k1);                     \
102 } while (0)
103
104 /* --- @SIPHASH_WORD@ --- *
105  *
106  * Arguments:   @kludge64 &a, &b, &c, &d@ = four state variables (updated)
107  *              @kludge64 m@ = message word (multiply evaluated)
108  *
109  * Returns:     ---
110  *
111  * Use:         Update a SipHash state with a single message word.
112  */
113
114 #define SIPHASH_WORD(a, b, c, d, m) do {                                \
115   XOR64((d), (d), (m));                                                 \
116   SIPHASH__ROUND(a, b, c, d); SIPHASH__ROUND(a, b, c, d);               \
117   XOR64((a), (a), (m));                                                 \
118 } while (0)
119
120 /* --- @SIPHASH_FINAL@ --- *
121  *
122  * Arguments:   @kludge64 &a, &b, &c, &d@ = four state variables (updated)
123  *              @kludge64 &z_out@ = hash result (output)
124  *              @const void *p@ = pointer to message tail
125  *              @unsigned n@ = length of message tail in bytes (must be less
126  *                      than @SIPHASH_BLKSZ@)
127  *              @size_t msz@ = overall message size (at least mod 256)
128  *
129  * Returns:     ---
130  *
131  * Use:         Completes a SipHash operation, storing the final result in
132  *              @z@.
133  */
134
135 #define SIPHASH_FINAL(a, b, c, d, z_out, p, n, msz) do {                \
136   const unsigned char *_p = (const unsigned char *)(p);                 \
137   kludge64 _t, _u;                                                      \
138                                                                         \
139   /* Prepare the final message word.                                    \
140    *                                                                    \
141    * This is kind of annoying.  Overrunning the input buffer could be   \
142    * disastrous.  This fiddly switch seems faster than building the     \
143    * value in a byte buffer and then loading it.                        \
144    */                                                                   \
145   SET64(_t, 0, 0); SETBYTE64(_t, (msz), 7);                             \
146   switch (n) {                                                          \
147     /* case 7: */                                                       \
148     default: SETBYTE64(_t, _p[6], 6);                                   \
149     case 6:  SETBYTE64(_t, _p[5], 5);                                   \
150     case 5:  SETBYTE64(_t, _p[4], 4);                                   \
151     case 4:  SETBYTE64(_t, _p[3], 3);                                   \
152     case 3:  SETBYTE64(_t, _p[2], 2);                                   \
153     case 2:  SETBYTE64(_t, _p[1], 1);                                   \
154     case 1:  SETBYTE64(_t, _p[0], 0);                                   \
155     case 0:  break;                                                     \
156   }                                                                     \
157                                                                         \
158   /* Finish the hashing job. */                                         \
159   SIPHASH_WORD(a, b, c, d, _t);                                         \
160   SET64(_t, 0, 0xff); XOR64((c), (c), _t);                              \
161   SIPHASH__ROUND(a, b, c, d); SIPHASH__ROUND(a, b, c, d);               \
162   SIPHASH__ROUND(a, b, c, d); SIPHASH__ROUND(a, b, c, d);               \
163                                                                         \
164   /* Combine the hash-state words into a single result. */              \
165   XOR64(_t, (a), (b)); XOR64(_u, (c), (d)); XOR64((z_out), _t, _u);     \
166   SET64((a), 0, 0); SET64((b), 0, 0); SET64((c), 0, 0); SET64((d), 0, 0); \
167 } while (0)
168
169 /* --- @SIPHASH@ --- *
170  *
171  * Arguments:   @const struct siphash_key *k@ = prepared key
172  *              @kludge64 &z_out@ = hash result (output)
173  *              @const void *p@ = pointer to message
174  *              @size_t sz@ = message size in bytes
175  *
176  * Returns:     ---
177  *
178  * Use:         Hash a message using under the key @k@, leaving the result in
179  *              @z@.
180  */
181
182 #define SIPHASH(k, z_out, p, sz) do {                                   \
183   kludge64 _a, _b, _c, _d, _m;                                          \
184   const octet *_q = (p); size_t _sz0 = (sz), _sz = _sz0;                \
185                                                                         \
186   SIPHASH_INIT(_a, _b, _c, _d, (k));                                    \
187   while (_sz >= SIPHASH_BLKSZ) {                                        \
188     LOAD64_L_(_m, _q); SIPHASH_WORD(_a, _b, _c, _d, _m);                \
189     _q += SIPHASH_BLKSZ; _sz -= SIPHASH_BLKSZ;                          \
190   }                                                                     \
191   SIPHASH_FINAL(_a, _b, _c, _d, (z_out), _q, _sz, _sz0);                \
192 } while (0)
193
194 /*----- Functions provided ------------------------------------------------*/
195
196 /* --- @siphash_setkey@ --- *
197  *
198  * Arguments:   @struct siphash_key *k@ = prepared key structure to fill in
199  *              @const octet *p@ = pointer to key data (@SIPHASH_KEYSZ = 16@
200  *                      bytes)
201  *
202  * Returns:     ---
203  *
204  * Use:         Prepare a SipHash key.
205  */
206
207 extern void siphash_setkey(struct siphash_key */*k*/, const octet */*p*/);
208
209 /* --- @siphash_init@ --- *
210  *
211  * Arguments:   @struct siphash *s@ = hashing state to initialize
212  *              @const struct siphash_key *k@ = prepared key structure
213  *
214  * Returns:     ---
215  *
216  * Use:         Initialize a state for hashing a message in multiple pieces.
217  */
218
219 extern void siphash_init(struct siphash */*s*/,
220                          const struct siphash_key */*k*/);
221
222 /* --- @siphash_hash@ --- *
223  *
224  * Arguments:   @struct siphash *s@ = hashing state
225  *              @const void *p, size_t sz@ = input message buffer
226  *
227  * Returns:     ---
228  *
229  * Use:         Update the hashing state by processing the provided input
230  *              message chunk.  The address and size do not need to be
231  *              aligned in any particular way.
232  */
233
234 extern void siphash_hash(struct siphash */*s*/,
235                          const void */*p*/, size_t /*sz*/);
236
237 /* --- @siphash_done@ --- *
238  *
239  * Arguments:   @struct siphash *s@ = hashing state (clobbered)
240  *
241  * Returns:     The completed hash.
242  *
243  * Use:         Complete the hashing operation tracked by the state,
244  *              returning the resulting 64-bit hash.
245  */
246
247 extern kludge64 siphash_done(struct siphash */*s*/);
248
249 /* --- @siphash@ --- *
250  *
251  * Arguments:   @const struct siphash_key *k@ = prepared key
252  *              @const void *p, size_t sz@ = input message buffer
253  *
254  * Returns:     The completed hash.
255  *
256  * Use:         Hash the message data in a single input buffer under the
257  *              control of the supplied key, returning the resulting hash.
258  *              This is simpler and faster than the @init@/@hash@/@done@
259  *              interface.
260  */
261
262 extern kludge64 siphash(const struct siphash_key */*k*/,
263                         const void */*p*/, size_t /*sz*/);
264
265 /*----- That's all, folks -------------------------------------------------*/
266
267 #ifdef __cplusplus
268   }
269 #endif
270
271 #endif