Commit | Line | Data |
---|---|---|
b1a20bee MW |
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 |