1 /*
2 SipHash reference C implementation
3
4 Copyright (c) 2012-2016 Jean-Philippe Aumasson
5 <[email protected]>
6 Copyright (c) 2012-2014 Daniel J. Bernstein <[email protected]>
7 Copyright (c) 2017 Salvatore Sanfilippo <[email protected]>
8
9 To the extent possible under law, the author(s) have dedicated all copyright
10 and related and neighboring rights to this software to the public domain
11 worldwide. This software is distributed without any warranty.
12
13 You should have received a copy of the CC0 Public Domain Dedication along
14 with this software. If not, see
15 <http://creativecommons.org/publicdomain/zero/1.0/>.
16
17 ----------------------------------------------------------------------------
18
19 This version was modified by Salvatore Sanfilippo <[email protected]>
20 in the following ways:
21
22 1. We use SipHash 1-2. This is not believed to be as strong as the
23 suggested 2-4 variant, but AFAIK there are not trivial attacks
24 against this reduced-rounds version, and it runs at the same speed
25 as Murmurhash2 that we used previously, why the 2-4 variant slowed
26 down Redis by a 4% figure more or less.
27 2. Hard-code rounds in the hope the compiler can optimize it more
28 in this raw from. Anyway we always want the standard 2-4 variant.
29 3. Modify the prototype and implementation so that the function directly
30 returns an uint64_t value, the hash itself, instead of receiving an
31 output buffer. This also means that the output size is set to 8 bytes
32 and the 16 bytes output code handling was removed.
33 4. Provide a case insensitive variant to be used when hashing strings that
34 must be considered identical by the hash table regardless of the case.
35 If we don't have directly a case insensitive hash function, we need to
36 perform a text transformation in some temporary buffer, which is costly.
37 5. Remove debugging code.
38 6. Modified the original test.c file to be a stand-alone function testing
39 the function in the new form (returing an uint64_t) using just the
40 relevant test vector.
41 */
42 #include <assert.h>
43 #include <stdint.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <ctype.h>
47
48 /* Fast tolower() alike function that does not care about locale
49 * but just returns a-z insetad of A-Z. */
siptlw(int c)50 int siptlw(int c) {
51 if (c >= 'A' && c <= 'Z') {
52 return c+('a'-'A');
53 } else {
54 return c;
55 }
56 }
57
58 /* Test of the CPU is Little Endian and supports not aligned accesses.
59 * Two interesting conditions to speedup the function that happen to be
60 * in most of x86 servers. */
61 #if defined(__X86_64__) || defined(__x86_64__) || defined (__i386__)
62 #define UNALIGNED_LE_CPU
63 #endif
64
65 #define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
66
67 #define U32TO8_LE(p, v) \
68 (p)[0] = (uint8_t)((v)); \
69 (p)[1] = (uint8_t)((v) >> 8); \
70 (p)[2] = (uint8_t)((v) >> 16); \
71 (p)[3] = (uint8_t)((v) >> 24);
72
73 #define U64TO8_LE(p, v) \
74 U32TO8_LE((p), (uint32_t)((v))); \
75 U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
76
77 #ifdef UNALIGNED_LE_CPU
78 #define U8TO64_LE(p) (*((uint64_t*)(p)))
79 #else
80 #define U8TO64_LE(p) \
81 (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \
82 ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
83 ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
84 ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
85 #endif
86
87 #define U8TO64_LE_NOCASE(p) \
88 (((uint64_t)(siptlw((p)[0]))) | \
89 ((uint64_t)(siptlw((p)[1])) << 8) | \
90 ((uint64_t)(siptlw((p)[2])) << 16) | \
91 ((uint64_t)(siptlw((p)[3])) << 24) | \
92 ((uint64_t)(siptlw((p)[4])) << 32) | \
93 ((uint64_t)(siptlw((p)[5])) << 40) | \
94 ((uint64_t)(siptlw((p)[6])) << 48) | \
95 ((uint64_t)(siptlw((p)[7])) << 56))
96
97 #define SIPROUND \
98 do { \
99 v0 += v1; \
100 v1 = ROTL(v1, 13); \
101 v1 ^= v0; \
102 v0 = ROTL(v0, 32); \
103 v2 += v3; \
104 v3 = ROTL(v3, 16); \
105 v3 ^= v2; \
106 v0 += v3; \
107 v3 = ROTL(v3, 21); \
108 v3 ^= v0; \
109 v2 += v1; \
110 v1 = ROTL(v1, 17); \
111 v1 ^= v2; \
112 v2 = ROTL(v2, 32); \
113 } while (0)
114
siphash(const uint8_t * in,const size_t inlen,const uint8_t * k)115 uint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k) {
116 #ifndef UNALIGNED_LE_CPU
117 uint64_t hash;
118 uint8_t *out = (uint8_t*) &hash;
119 #endif
120 uint64_t v0 = 0x736f6d6570736575ULL;
121 uint64_t v1 = 0x646f72616e646f6dULL;
122 uint64_t v2 = 0x6c7967656e657261ULL;
123 uint64_t v3 = 0x7465646279746573ULL;
124 uint64_t k0 = U8TO64_LE(k);
125 uint64_t k1 = U8TO64_LE(k + 8);
126 uint64_t m;
127 const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));
128 const int left = inlen & 7;
129 uint64_t b = ((uint64_t)inlen) << 56;
130 v3 ^= k1;
131 v2 ^= k0;
132 v1 ^= k1;
133 v0 ^= k0;
134
135 for (; in != end; in += 8) {
136 m = U8TO64_LE(in);
137 v3 ^= m;
138
139 SIPROUND;
140
141 v0 ^= m;
142 }
143
144 switch (left) {
145 case 7: b |= ((uint64_t)in[6]) << 48; /* fall-thru */
146 case 6: b |= ((uint64_t)in[5]) << 40; /* fall-thru */
147 case 5: b |= ((uint64_t)in[4]) << 32; /* fall-thru */
148 case 4: b |= ((uint64_t)in[3]) << 24; /* fall-thru */
149 case 3: b |= ((uint64_t)in[2]) << 16; /* fall-thru */
150 case 2: b |= ((uint64_t)in[1]) << 8; /* fall-thru */
151 case 1: b |= ((uint64_t)in[0]); break;
152 case 0: break;
153 }
154
155 v3 ^= b;
156
157 SIPROUND;
158
159 v0 ^= b;
160 v2 ^= 0xff;
161
162 SIPROUND;
163 SIPROUND;
164
165 b = v0 ^ v1 ^ v2 ^ v3;
166 #ifndef UNALIGNED_LE_CPU
167 U64TO8_LE(out, b);
168 return hash;
169 #else
170 return b;
171 #endif
172 }
173
siphash_nocase(const uint8_t * in,const size_t inlen,const uint8_t * k)174 uint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k)
175 {
176 #ifndef UNALIGNED_LE_CPU
177 uint64_t hash;
178 uint8_t *out = (uint8_t*) &hash;
179 #endif
180 uint64_t v0 = 0x736f6d6570736575ULL;
181 uint64_t v1 = 0x646f72616e646f6dULL;
182 uint64_t v2 = 0x6c7967656e657261ULL;
183 uint64_t v3 = 0x7465646279746573ULL;
184 uint64_t k0 = U8TO64_LE(k);
185 uint64_t k1 = U8TO64_LE(k + 8);
186 uint64_t m;
187 const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));
188 const int left = inlen & 7;
189 uint64_t b = ((uint64_t)inlen) << 56;
190 v3 ^= k1;
191 v2 ^= k0;
192 v1 ^= k1;
193 v0 ^= k0;
194
195 for (; in != end; in += 8) {
196 m = U8TO64_LE_NOCASE(in);
197 v3 ^= m;
198
199 SIPROUND;
200
201 v0 ^= m;
202 }
203
204 switch (left) {
205 case 7: b |= ((uint64_t)siptlw(in[6])) << 48; /* fall-thru */
206 case 6: b |= ((uint64_t)siptlw(in[5])) << 40; /* fall-thru */
207 case 5: b |= ((uint64_t)siptlw(in[4])) << 32; /* fall-thru */
208 case 4: b |= ((uint64_t)siptlw(in[3])) << 24; /* fall-thru */
209 case 3: b |= ((uint64_t)siptlw(in[2])) << 16; /* fall-thru */
210 case 2: b |= ((uint64_t)siptlw(in[1])) << 8; /* fall-thru */
211 case 1: b |= ((uint64_t)siptlw(in[0])); break;
212 case 0: break;
213 }
214
215 v3 ^= b;
216
217 SIPROUND;
218
219 v0 ^= b;
220 v2 ^= 0xff;
221
222 SIPROUND;
223 SIPROUND;
224
225 b = v0 ^ v1 ^ v2 ^ v3;
226 #ifndef UNALIGNED_LE_CPU
227 U64TO8_LE(out, b);
228 return hash;
229 #else
230 return b;
231 #endif
232 }
233
234
235 /* --------------------------------- TEST ------------------------------------ */
236
237 #ifdef SIPHASH_TEST
238
239 const uint8_t vectors_sip64[64][8] = {
240 { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, },
241 { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, },
242 { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, },
243 { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, },
244 { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, },
245 { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, },
246 { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, },
247 { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, },
248 { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, },
249 { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, },
250 { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, },
251 { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, },
252 { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, },
253 { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, },
254 { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, },
255 { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, },
256 { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, },
257 { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, },
258 { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, },
259 { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, },
260 { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, },
261 { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, },
262 { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, },
263 { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, },
264 { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, },
265 { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, },
266 { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, },
267 { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, },
268 { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, },
269 { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, },
270 { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, },
271 { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, },
272 { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, },
273 { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, },
274 { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, },
275 { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, },
276 { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, },
277 { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, },
278 { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, },
279 { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, },
280 { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, },
281 { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, },
282 { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, },
283 { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, },
284 { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, },
285 { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, },
286 { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, },
287 { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, },
288 { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, },
289 { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, },
290 { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, },
291 { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, },
292 { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, },
293 { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, },
294 { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, },
295 { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, },
296 { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, },
297 { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, },
298 { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, },
299 { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, },
300 { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, },
301 { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, },
302 { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, },
303 { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, },
304 };
305
306
307 /* Test siphash using a test vector. Returns 0 if the function passed
308 * all the tests, otherwise 1 is returned.
309 *
310 * IMPORTANT: The test vector is for SipHash 2-4. Before running
311 * the test revert back the siphash() function to 2-4 rounds since
312 * now it uses 1-2 rounds. */
siphash_test(void)313 int siphash_test(void) {
314 uint8_t in[64], k[16];
315 int i;
316 int fails = 0;
317
318 for (i = 0; i < 16; ++i)
319 k[i] = i;
320
321 for (i = 0; i < 64; ++i) {
322 in[i] = i;
323 uint64_t hash = siphash(in, i, k);
324 const uint8_t *v = NULL;
325 v = (uint8_t *)vectors_sip64;
326 if (memcmp(&hash, v + (i * 8), 8)) {
327 /* printf("fail for %d bytes\n", i); */
328 fails++;
329 }
330 }
331
332 /* Run a few basic tests with the case insensitive version. */
333 uint64_t h1, h2;
334 h1 = siphash((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678");
335 h2 = siphash_nocase((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678");
336 if (h1 != h2) fails++;
337
338 h1 = siphash((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678");
339 h2 = siphash_nocase((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678");
340 if (h1 != h2) fails++;
341
342 h1 = siphash((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678");
343 h2 = siphash_nocase((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678");
344 if (h1 == h2) fails++;
345
346 if (!fails) return 0;
347 return 1;
348 }
349
main(void)350 int main(void) {
351 if (siphash_test() == 0) {
352 printf("SipHash test: OK\n");
353 return 0;
354 } else {
355 printf("SipHash test: FAILED\n");
356 return 1;
357 }
358 }
359
360 #endif
361