authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2020-08-22 01:11:24+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-22 02:47:50-04:00
loge919744c7ad2b224bef00ffc35ccacbe31a0aae7
tree52c9ef41697ecc2d7dcbf46e022127e43f3e47b1
parent9ab428185688896ab5b6d4bc924366d22bfe4ccd

Promote hash/siphash to crypto/siphash

SipHash *is* a cryptographic function, with a 128-bit security level. However, it is not a regular hash function: a secret key is required, and knowledge of that key allows collisions to be quickly computed offline. SipHash is therefore more suitable to be used as a MAC. The same API as other MACs was implemented in addition to functions directly returning an integer. The benchmarks have been updated accordingly. No changes to the SipHash implementation itself.

6 files changed, 433 insertions(+), 407 deletions(-)

lib/std/crypto.zig+2
......@@ -18,6 +18,7 @@ pub const hash = struct {
1818/// Authentication (MAC) functions.
1919pub const auth = struct {
2020 pub const hmac = @import("crypto/hmac.zig");
21 pub const siphash = @import("crypto/siphash.zig");
2122};
2223
2324/// Authenticated Encryption with Associated Data
......@@ -80,6 +81,7 @@ test "crypto" {
8081 _ = @import("crypto/sha1.zig");
8182 _ = @import("crypto/sha2.zig");
8283 _ = @import("crypto/sha3.zig");
84 _ = @import("crypto/siphash.zig");
8385 _ = @import("crypto/25519/curve25519.zig");
8486 _ = @import("crypto/25519/ed25519.zig");
8587 _ = @import("crypto/25519/edwards25519.zig");
lib/std/crypto/benchmark.zig+4
......@@ -60,6 +60,10 @@ const macs = [_]Crypto{
6060 Crypto{ .ty = crypto.auth.hmac.HmacSha1, .name = "hmac-sha1" },
6161 Crypto{ .ty = crypto.auth.hmac.sha2.HmacSha256, .name = "hmac-sha256" },
6262 Crypto{ .ty = crypto.auth.hmac.sha2.HmacSha512, .name = "hmac-sha512" },
63 Crypto{ .ty = crypto.auth.siphash.SipHash64(2, 4), .name = "siphash-2-4" },
64 Crypto{ .ty = crypto.auth.siphash.SipHash64(1, 3), .name = "siphash-1-3" },
65 Crypto{ .ty = crypto.auth.siphash.SipHash128(2, 4), .name = "siphash128-2-4" },
66 Crypto{ .ty = crypto.auth.siphash.SipHash128(1, 3), .name = "siphash128-1-3" },
6367};
6468
6569pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {
lib/std/crypto/siphash.zig created+426
......@@ -0,0 +1,426 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6//
7// SipHash is a moderately fast pseudorandom function, returning a 64-bit or 128-bit tag for an arbitrary long input.
8//
9// Typical use cases include:
10// - protection against against DoS attacks for hash tables and bloom filters
11// - authentication of short-lived messages in online protocols
12//
13// https://131002.net/siphash/
14const std = @import("../std.zig");
15const assert = std.debug.assert;
16const testing = std.testing;
17const math = std.math;
18const mem = std.mem;
19
20/// SipHash function with 64-bit output.
21///
22/// Recommended parameters are:
23/// - (c_rounds=2, d_rounds=4) standard parameters.
24/// - (c_rounds=1, d_rounds=2) reduced-round function. Faster, no known implications on its practical security level.
25///
26/// SipHash is not a traditional hash function. If the input includes untrusted content, a secret key is absolutely necessary.
27/// And due to its small output size, collisions in SipHash64 can be found with an exhaustive search.
28pub fn SipHash64(comptime c_rounds: usize, comptime d_rounds: usize) type {
29 return SipHash(u64, c_rounds, d_rounds);
30}
31
32/// SipHash function with 128-bit output.
33///
34/// Recommended parameters are:
35/// - (c_rounds=2, d_rounds=4) standard parameters.
36/// - (c_rounds=1, d_rounds=2) reduced-round function. Faster, no known implications on its practical security level.
37///
38/// SipHash is not a traditional hash function. If the input includes untrusted content, a secret key is absolutely necessary.
39pub fn SipHash128(comptime c_rounds: usize, comptime d_rounds: usize) type {
40 return SipHash(u128, c_rounds, d_rounds);
41}
42
43fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type {
44 assert(T == u64 or T == u128);
45 assert(c_rounds > 0 and d_rounds > 0);
46
47 return struct {
48 const Self = @This();
49 const digest_size = 64;
50 const block_size = 64;
51
52 v0: u64,
53 v1: u64,
54 v2: u64,
55 v3: u64,
56 msg_len: u8,
57
58 pub fn init(key: []const u8) Self {
59 assert(key.len >= 16);
60
61 const k0 = mem.readIntLittle(u64, key[0..8]);
62 const k1 = mem.readIntLittle(u64, key[8..16]);
63
64 var d = Self{
65 .v0 = k0 ^ 0x736f6d6570736575,
66 .v1 = k1 ^ 0x646f72616e646f6d,
67 .v2 = k0 ^ 0x6c7967656e657261,
68 .v3 = k1 ^ 0x7465646279746573,
69 .msg_len = 0,
70 };
71
72 if (T == u128) {
73 d.v1 ^= 0xee;
74 }
75
76 return d;
77 }
78
79 pub fn update(self: *Self, b: []const u8) void {
80 std.debug.assert(b.len % 8 == 0);
81
82 var off: usize = 0;
83 while (off < b.len) : (off += 8) {
84 @call(.{ .modifier = .always_inline }, self.round, .{b[off .. off + 8]});
85 }
86
87 self.msg_len +%= @truncate(u8, b.len);
88 }
89
90 pub fn final(self: *Self, b: []const u8) T {
91 std.debug.assert(b.len < 8);
92
93 self.msg_len +%= @truncate(u8, b.len);
94
95 var buf = [_]u8{0} ** 8;
96 mem.copy(u8, buf[0..], b[0..]);
97 buf[7] = self.msg_len;
98 self.round(buf[0..]);
99
100 if (T == u128) {
101 self.v2 ^= 0xee;
102 } else {
103 self.v2 ^= 0xff;
104 }
105
106 // TODO this is a workaround, should be able to supply the value without a separate variable
107 const inl = std.builtin.CallOptions{ .modifier = .always_inline };
108
109 comptime var i: usize = 0;
110 inline while (i < d_rounds) : (i += 1) {
111 @call(inl, sipRound, .{self});
112 }
113
114 const b1 = self.v0 ^ self.v1 ^ self.v2 ^ self.v3;
115 if (T == u64) {
116 return b1;
117 }
118
119 self.v1 ^= 0xdd;
120
121 comptime var j: usize = 0;
122 inline while (j < d_rounds) : (j += 1) {
123 @call(inl, sipRound, .{self});
124 }
125
126 const b2 = self.v0 ^ self.v1 ^ self.v2 ^ self.v3;
127 return (@as(u128, b2) << 64) | b1;
128 }
129
130 fn round(self: *Self, b: []const u8) void {
131 assert(b.len == 8);
132
133 const m = mem.readIntLittle(u64, b[0..8]);
134 self.v3 ^= m;
135
136 // TODO this is a workaround, should be able to supply the value without a separate variable
137 const inl = std.builtin.CallOptions{ .modifier = .always_inline };
138 comptime var i: usize = 0;
139 inline while (i < c_rounds) : (i += 1) {
140 @call(inl, sipRound, .{self});
141 }
142
143 self.v0 ^= m;
144 }
145
146 fn sipRound(d: *Self) void {
147 d.v0 +%= d.v1;
148 d.v1 = math.rotl(u64, d.v1, @as(u64, 13));
149 d.v1 ^= d.v0;
150 d.v0 = math.rotl(u64, d.v0, @as(u64, 32));
151 d.v2 +%= d.v3;
152 d.v3 = math.rotl(u64, d.v3, @as(u64, 16));
153 d.v3 ^= d.v2;
154 d.v0 +%= d.v3;
155 d.v3 = math.rotl(u64, d.v3, @as(u64, 21));
156 d.v3 ^= d.v0;
157 d.v2 +%= d.v1;
158 d.v1 = math.rotl(u64, d.v1, @as(u64, 17));
159 d.v1 ^= d.v2;
160 d.v2 = math.rotl(u64, d.v2, @as(u64, 32));
161 }
162
163 pub fn hash(msg: []const u8, key: []const u8) T {
164 const aligned_len = msg.len - (msg.len % 8);
165 var c = Self.init(key);
166 @call(.{ .modifier = .always_inline }, c.update, .{msg[0..aligned_len]});
167 return @call(.{ .modifier = .always_inline }, c.final, .{msg[aligned_len..]});
168 }
169 };
170}
171
172fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type {
173 assert(T == u64 or T == u128);
174 assert(c_rounds > 0 and d_rounds > 0);
175
176 return struct {
177 const State = SipHashStateless(T, c_rounds, d_rounds);
178 const Self = @This();
179 pub const minimum_key_length = 16;
180 pub const mac_length = @sizeOf(T);
181 pub const block_length = 8;
182
183 state: State,
184 buf: [8]u8,
185 buf_len: usize,
186
187 /// Initialize a state for a SipHash function
188 pub fn init(key: []const u8) Self {
189 return Self{
190 .state = State.init(key),
191 .buf = undefined,
192 .buf_len = 0,
193 };
194 }
195
196 /// Add data to the state
197 pub fn update(self: *Self, b: []const u8) void {
198 var off: usize = 0;
199
200 if (self.buf_len != 0 and self.buf_len + b.len >= 8) {
201 off += 8 - self.buf_len;
202 mem.copy(u8, self.buf[self.buf_len..], b[0..off]);
203 self.state.update(self.buf[0..]);
204 self.buf_len = 0;
205 }
206
207 const remain_len = b.len - off;
208 const aligned_len = remain_len - (remain_len % 8);
209 self.state.update(b[off .. off + aligned_len]);
210
211 mem.copy(u8, self.buf[self.buf_len..], b[off + aligned_len ..]);
212 self.buf_len += @intCast(u8, b[off + aligned_len ..].len);
213 }
214
215 /// Return an authentication tag for the current state
216 pub fn final(self: *Self, out: []u8) void {
217 std.debug.assert(out.len >= mac_length);
218 mem.writeIntLittle(T, out[0..mac_length], self.state.final(self.buf[0..self.buf_len]));
219 }
220
221 /// Return an authentication tag for a message and a key
222 pub fn create(out: []u8, msg: []const u8, key: []const u8) void {
223 var ctx = Self.init(key);
224 ctx.update(msg);
225 ctx.final(out[0..]);
226 }
227
228 /// Return an authentication tag for the current state, as an integer
229 pub fn finalInt(self: *Self) T {
230 return self.state.final(self.buf[0..self.buf_len]);
231 }
232
233 /// Return an authentication tag for a message and a key, as an integer
234 pub fn toInt(msg: []const u8, key: []const u8) T {
235 return State.hash(msg, key);
236 }
237 };
238}
239
240// Test vectors from reference implementation.
241// https://github.com/veorq/SipHash/blob/master/vectors.h
242const test_key = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";
243
244test "siphash64-2-4 sanity" {
245 const vectors = [_][8]u8{
246 "\x31\x0e\x0e\xdd\x47\xdb\x6f\x72".*, // ""
247 "\xfd\x67\xdc\x93\xc5\x39\xf8\x74".*, // "\x00"
248 "\x5a\x4f\xa9\xd9\x09\x80\x6c\x0d".*, // "\x00\x01" ... etc
249 "\x2d\x7e\xfb\xd7\x96\x66\x67\x85".*,
250 "\xb7\x87\x71\x27\xe0\x94\x27\xcf".*,
251 "\x8d\xa6\x99\xcd\x64\x55\x76\x18".*,
252 "\xce\xe3\xfe\x58\x6e\x46\xc9\xcb".*,
253 "\x37\xd1\x01\x8b\xf5\x00\x02\xab".*,
254 "\x62\x24\x93\x9a\x79\xf5\xf5\x93".*,
255 "\xb0\xe4\xa9\x0b\xdf\x82\x00\x9e".*,
256 "\xf3\xb9\xdd\x94\xc5\xbb\x5d\x7a".*,
257 "\xa7\xad\x6b\x22\x46\x2f\xb3\xf4".*,
258 "\xfb\xe5\x0e\x86\xbc\x8f\x1e\x75".*,
259 "\x90\x3d\x84\xc0\x27\x56\xea\x14".*,
260 "\xee\xf2\x7a\x8e\x90\xca\x23\xf7".*,
261 "\xe5\x45\xbe\x49\x61\xca\x29\xa1".*,
262 "\xdb\x9b\xc2\x57\x7f\xcc\x2a\x3f".*,
263 "\x94\x47\xbe\x2c\xf5\xe9\x9a\x69".*,
264 "\x9c\xd3\x8d\x96\xf0\xb3\xc1\x4b".*,
265 "\xbd\x61\x79\xa7\x1d\xc9\x6d\xbb".*,
266 "\x98\xee\xa2\x1a\xf2\x5c\xd6\xbe".*,
267 "\xc7\x67\x3b\x2e\xb0\xcb\xf2\xd0".*,
268 "\x88\x3e\xa3\xe3\x95\x67\x53\x93".*,
269 "\xc8\xce\x5c\xcd\x8c\x03\x0c\xa8".*,
270 "\x94\xaf\x49\xf6\xc6\x50\xad\xb8".*,
271 "\xea\xb8\x85\x8a\xde\x92\xe1\xbc".*,
272 "\xf3\x15\xbb\x5b\xb8\x35\xd8\x17".*,
273 "\xad\xcf\x6b\x07\x63\x61\x2e\x2f".*,
274 "\xa5\xc9\x1d\xa7\xac\xaa\x4d\xde".*,
275 "\x71\x65\x95\x87\x66\x50\xa2\xa6".*,
276 "\x28\xef\x49\x5c\x53\xa3\x87\xad".*,
277 "\x42\xc3\x41\xd8\xfa\x92\xd8\x32".*,
278 "\xce\x7c\xf2\x72\x2f\x51\x27\x71".*,
279 "\xe3\x78\x59\xf9\x46\x23\xf3\xa7".*,
280 "\x38\x12\x05\xbb\x1a\xb0\xe0\x12".*,
281 "\xae\x97\xa1\x0f\xd4\x34\xe0\x15".*,
282 "\xb4\xa3\x15\x08\xbe\xff\x4d\x31".*,
283 "\x81\x39\x62\x29\xf0\x90\x79\x02".*,
284 "\x4d\x0c\xf4\x9e\xe5\xd4\xdc\xca".*,
285 "\x5c\x73\x33\x6a\x76\xd8\xbf\x9a".*,
286 "\xd0\xa7\x04\x53\x6b\xa9\x3e\x0e".*,
287 "\x92\x59\x58\xfc\xd6\x42\x0c\xad".*,
288 "\xa9\x15\xc2\x9b\xc8\x06\x73\x18".*,
289 "\x95\x2b\x79\xf3\xbc\x0a\xa6\xd4".*,
290 "\xf2\x1d\xf2\xe4\x1d\x45\x35\xf9".*,
291 "\x87\x57\x75\x19\x04\x8f\x53\xa9".*,
292 "\x10\xa5\x6c\xf5\xdf\xcd\x9a\xdb".*,
293 "\xeb\x75\x09\x5c\xcd\x98\x6c\xd0".*,
294 "\x51\xa9\xcb\x9e\xcb\xa3\x12\xe6".*,
295 "\x96\xaf\xad\xfc\x2c\xe6\x66\xc7".*,
296 "\x72\xfe\x52\x97\x5a\x43\x64\xee".*,
297 "\x5a\x16\x45\xb2\x76\xd5\x92\xa1".*,
298 "\xb2\x74\xcb\x8e\xbf\x87\x87\x0a".*,
299 "\x6f\x9b\xb4\x20\x3d\xe7\xb3\x81".*,
300 "\xea\xec\xb2\xa3\x0b\x22\xa8\x7f".*,
301 "\x99\x24\xa4\x3c\xc1\x31\x57\x24".*,
302 "\xbd\x83\x8d\x3a\xaf\xbf\x8d\xb7".*,
303 "\x0b\x1a\x2a\x32\x65\xd5\x1a\xea".*,
304 "\x13\x50\x79\xa3\x23\x1c\xe6\x60".*,
305 "\x93\x2b\x28\x46\xe4\xd7\x06\x66".*,
306 "\xe1\x91\x5f\x5c\xb1\xec\xa4\x6c".*,
307 "\xf3\x25\x96\x5c\xa1\x6d\x62\x9f".*,
308 "\x57\x5f\xf2\x8e\x60\x38\x1b\xe5".*,
309 "\x72\x45\x06\xeb\x4c\x32\x8a\x95".*,
310 };
311
312 const siphash = SipHash64(2, 4);
313
314 var buffer: [64]u8 = undefined;
315 for (vectors) |vector, i| {
316 buffer[i] = @intCast(u8, i);
317
318 var out: [siphash.mac_length]u8 = undefined;
319 siphash.create(&out, buffer[0..i], test_key);
320 testing.expectEqual(out, vector);
321 }
322}
323
324test "siphash128-2-4 sanity" {
325 const vectors = [_][16]u8{
326 "\xa3\x81\x7f\x04\xba\x25\xa8\xe6\x6d\xf6\x72\x14\xc7\x55\x02\x93".*,
327 "\xda\x87\xc1\xd8\x6b\x99\xaf\x44\x34\x76\x59\x11\x9b\x22\xfc\x45".*,
328 "\x81\x77\x22\x8d\xa4\xa4\x5d\xc7\xfc\xa3\x8b\xde\xf6\x0a\xff\xe4".*,
329 "\x9c\x70\xb6\x0c\x52\x67\xa9\x4e\x5f\x33\xb6\xb0\x29\x85\xed\x51".*,
330 "\xf8\x81\x64\xc1\x2d\x9c\x8f\xaf\x7d\x0f\x6e\x7c\x7b\xcd\x55\x79".*,
331 "\x13\x68\x87\x59\x80\x77\x6f\x88\x54\x52\x7a\x07\x69\x0e\x96\x27".*,
332 "\x14\xee\xca\x33\x8b\x20\x86\x13\x48\x5e\xa0\x30\x8f\xd7\xa1\x5e".*,
333 "\xa1\xf1\xeb\xbe\xd8\xdb\xc1\x53\xc0\xb8\x4a\xa6\x1f\xf0\x82\x39".*,
334 "\x3b\x62\xa9\xba\x62\x58\xf5\x61\x0f\x83\xe2\x64\xf3\x14\x97\xb4".*,
335 "\x26\x44\x99\x06\x0a\xd9\xba\xab\xc4\x7f\x8b\x02\xbb\x6d\x71\xed".*,
336 "\x00\x11\x0d\xc3\x78\x14\x69\x56\xc9\x54\x47\xd3\xf3\xd0\xfb\xba".*,
337 "\x01\x51\xc5\x68\x38\x6b\x66\x77\xa2\xb4\xdc\x6f\x81\xe5\xdc\x18".*,
338 "\xd6\x26\xb2\x66\x90\x5e\xf3\x58\x82\x63\x4d\xf6\x85\x32\xc1\x25".*,
339 "\x98\x69\xe2\x47\xe9\xc0\x8b\x10\xd0\x29\x93\x4f\xc4\xb9\x52\xf7".*,
340 "\x31\xfc\xef\xac\x66\xd7\xde\x9c\x7e\xc7\x48\x5f\xe4\x49\x49\x02".*,
341 "\x54\x93\xe9\x99\x33\xb0\xa8\x11\x7e\x08\xec\x0f\x97\xcf\xc3\xd9".*,
342 "\x6e\xe2\xa4\xca\x67\xb0\x54\xbb\xfd\x33\x15\xbf\x85\x23\x05\x77".*,
343 "\x47\x3d\x06\xe8\x73\x8d\xb8\x98\x54\xc0\x66\xc4\x7a\xe4\x77\x40".*,
344 "\xa4\x26\xe5\xe4\x23\xbf\x48\x85\x29\x4d\xa4\x81\xfe\xae\xf7\x23".*,
345 "\x78\x01\x77\x31\xcf\x65\xfa\xb0\x74\xd5\x20\x89\x52\x51\x2e\xb1".*,
346 "\x9e\x25\xfc\x83\x3f\x22\x90\x73\x3e\x93\x44\xa5\xe8\x38\x39\xeb".*,
347 "\x56\x8e\x49\x5a\xbe\x52\x5a\x21\x8a\x22\x14\xcd\x3e\x07\x1d\x12".*,
348 "\x4a\x29\xb5\x45\x52\xd1\x6b\x9a\x46\x9c\x10\x52\x8e\xff\x0a\xae".*,
349 "\xc9\xd1\x84\xdd\xd5\xa9\xf5\xe0\xcf\x8c\xe2\x9a\x9a\xbf\x69\x1c".*,
350 "\x2d\xb4\x79\xae\x78\xbd\x50\xd8\x88\x2a\x8a\x17\x8a\x61\x32\xad".*,
351 "\x8e\xce\x5f\x04\x2d\x5e\x44\x7b\x50\x51\xb9\xea\xcb\x8d\x8f\x6f".*,
352 "\x9c\x0b\x53\xb4\xb3\xc3\x07\xe8\x7e\xae\xe0\x86\x78\x14\x1f\x66".*,
353 "\xab\xf2\x48\xaf\x69\xa6\xea\xe4\xbf\xd3\xeb\x2f\x12\x9e\xeb\x94".*,
354 "\x06\x64\xda\x16\x68\x57\x4b\x88\xb9\x35\xf3\x02\x73\x58\xae\xf4".*,
355 "\xaa\x4b\x9d\xc4\xbf\x33\x7d\xe9\x0c\xd4\xfd\x3c\x46\x7c\x6a\xb7".*,
356 "\xea\x5c\x7f\x47\x1f\xaf\x6b\xde\x2b\x1a\xd7\xd4\x68\x6d\x22\x87".*,
357 "\x29\x39\xb0\x18\x32\x23\xfa\xfc\x17\x23\xde\x4f\x52\xc4\x3d\x35".*,
358 "\x7c\x39\x56\xca\x5e\xea\xfc\x3e\x36\x3e\x9d\x55\x65\x46\xeb\x68".*,
359 "\x77\xc6\x07\x71\x46\xf0\x1c\x32\xb6\xb6\x9d\x5f\x4e\xa9\xff\xcf".*,
360 "\x37\xa6\x98\x6c\xb8\x84\x7e\xdf\x09\x25\xf0\xf1\x30\x9b\x54\xde".*,
361 "\xa7\x05\xf0\xe6\x9d\xa9\xa8\xf9\x07\x24\x1a\x2e\x92\x3c\x8c\xc8".*,
362 "\x3d\xc4\x7d\x1f\x29\xc4\x48\x46\x1e\x9e\x76\xed\x90\x4f\x67\x11".*,
363 "\x0d\x62\xbf\x01\xe6\xfc\x0e\x1a\x0d\x3c\x47\x51\xc5\xd3\x69\x2b".*,
364 "\x8c\x03\x46\x8b\xca\x7c\x66\x9e\xe4\xfd\x5e\x08\x4b\xbe\xe7\xb5".*,
365 "\x52\x8a\x5b\xb9\x3b\xaf\x2c\x9c\x44\x73\xcc\xe5\xd0\xd2\x2b\xd9".*,
366 "\xdf\x6a\x30\x1e\x95\xc9\x5d\xad\x97\xae\x0c\xc8\xc6\x91\x3b\xd8".*,
367 "\x80\x11\x89\x90\x2c\x85\x7f\x39\xe7\x35\x91\x28\x5e\x70\xb6\xdb".*,
368 "\xe6\x17\x34\x6a\xc9\xc2\x31\xbb\x36\x50\xae\x34\xcc\xca\x0c\x5b".*,
369 "\x27\xd9\x34\x37\xef\xb7\x21\xaa\x40\x18\x21\xdc\xec\x5a\xdf\x89".*,
370 "\x89\x23\x7d\x9d\xed\x9c\x5e\x78\xd8\xb1\xc9\xb1\x66\xcc\x73\x42".*,
371 "\x4a\x6d\x80\x91\xbf\x5e\x7d\x65\x11\x89\xfa\x94\xa2\x50\xb1\x4c".*,
372 "\x0e\x33\xf9\x60\x55\xe7\xae\x89\x3f\xfc\x0e\x3d\xcf\x49\x29\x02".*,
373 "\xe6\x1c\x43\x2b\x72\x0b\x19\xd1\x8e\xc8\xd8\x4b\xdc\x63\x15\x1b".*,
374 "\xf7\xe5\xae\xf5\x49\xf7\x82\xcf\x37\x90\x55\xa6\x08\x26\x9b\x16".*,
375 "\x43\x8d\x03\x0f\xd0\xb7\xa5\x4f\xa8\x37\xf2\xad\x20\x1a\x64\x03".*,
376 "\xa5\x90\xd3\xee\x4f\xbf\x04\xe3\x24\x7e\x0d\x27\xf2\x86\x42\x3f".*,
377 "\x5f\xe2\xc1\xa1\x72\xfe\x93\xc4\xb1\x5c\xd3\x7c\xae\xf9\xf5\x38".*,
378 "\x2c\x97\x32\x5c\xbd\x06\xb3\x6e\xb2\x13\x3d\xd0\x8b\x3a\x01\x7c".*,
379 "\x92\xc8\x14\x22\x7a\x6b\xca\x94\x9f\xf0\x65\x9f\x00\x2a\xd3\x9e".*,
380 "\xdc\xe8\x50\x11\x0b\xd8\x32\x8c\xfb\xd5\x08\x41\xd6\x91\x1d\x87".*,
381 "\x67\xf1\x49\x84\xc7\xda\x79\x12\x48\xe3\x2b\xb5\x92\x25\x83\xda".*,
382 "\x19\x38\xf2\xcf\x72\xd5\x4e\xe9\x7e\x94\x16\x6f\xa9\x1d\x2a\x36".*,
383 "\x74\x48\x1e\x96\x46\xed\x49\xfe\x0f\x62\x24\x30\x16\x04\x69\x8e".*,
384 "\x57\xfc\xa5\xde\x98\xa9\xd6\xd8\x00\x64\x38\xd0\x58\x3d\x8a\x1d".*,
385 "\x9f\xec\xde\x1c\xef\xdc\x1c\xbe\xd4\x76\x36\x74\xd9\x57\x53\x59".*,
386 "\xe3\x04\x0c\x00\xeb\x28\xf1\x53\x66\xca\x73\xcb\xd8\x72\xe7\x40".*,
387 "\x76\x97\x00\x9a\x6a\x83\x1d\xfe\xcc\xa9\x1c\x59\x93\x67\x0f\x7a".*,
388 "\x58\x53\x54\x23\x21\xf5\x67\xa0\x05\xd5\x47\xa4\xf0\x47\x59\xbd".*,
389 "\x51\x50\xd1\x77\x2f\x50\x83\x4a\x50\x3e\x06\x9a\x97\x3f\xbd\x7c".*,
390 };
391
392 const siphash = SipHash128(2, 4);
393
394 var buffer: [64]u8 = undefined;
395 for (vectors) |vector, i| {
396 buffer[i] = @intCast(u8, i);
397
398 var out: [siphash.mac_length]u8 = undefined;
399 siphash.create(&out, buffer[0..i], test_key[0..]);
400 testing.expectEqual(out, vector);
401 }
402}
403
404test "iterative non-divisible update" {
405 var buf: [1024]u8 = undefined;
406 for (buf) |*e, i| {
407 e.* = @truncate(u8, i);
408 }
409
410 const key = "0x128dad08f12307";
411 const Siphash = SipHash64(2, 4);
412
413 var end: usize = 9;
414 while (end < buf.len) : (end += 9) {
415 const non_iterative_hash = Siphash.toInt(buf[0..end], key[0..]);
416
417 var siphash = Siphash.init(key);
418 var i: usize = 0;
419 while (i < end) : (i += 7) {
420 siphash.update(buf[i..std.math.min(i + 7, end)]);
421 }
422 const iterative_hash = siphash.finalInt();
423
424 std.testing.expectEqual(iterative_hash, non_iterative_hash);
425 }
426}
lib/std/hash.zig+1-2
......@@ -20,7 +20,7 @@ pub const Fnv1a_32 = fnv.Fnv1a_32;
2020pub const Fnv1a_64 = fnv.Fnv1a_64;
2121pub const Fnv1a_128 = fnv.Fnv1a_128;
2222
23const siphash = @import("hash/siphash.zig");
23const siphash = @import("crypto/siphash.zig");
2424pub const SipHash64 = siphash.SipHash64;
2525pub const SipHash128 = siphash.SipHash128;
2626
......@@ -42,7 +42,6 @@ test "hash" {
4242 _ = @import("hash/auto_hash.zig");
4343 _ = @import("hash/crc.zig");
4444 _ = @import("hash/fnv.zig");
45 _ = @import("hash/siphash.zig");
4645 _ = @import("hash/murmur.zig");
4746 _ = @import("hash/cityhash.zig");
4847 _ = @import("hash/wyhash.zig");
lib/std/hash/benchmark.zig-12
......@@ -25,24 +25,12 @@ const Hash = struct {
2525 init_u64: ?u64 = null,
2626};
2727
28const siphash_key = "0123456789abcdef";
29
3028const hashes = [_]Hash{
3129 Hash{
3230 .ty = hash.Wyhash,
3331 .name = "wyhash",
3432 .init_u64 = 0,
3533 },
36 Hash{
37 .ty = hash.SipHash64(1, 3),
38 .name = "siphash(1,3)",
39 .init_u8s = siphash_key,
40 },
41 Hash{
42 .ty = hash.SipHash64(2, 4),
43 .name = "siphash(2,4)",
44 .init_u8s = siphash_key,
45 },
4634 Hash{
4735 .ty = hash.Fnv1a_64,
4836 .name = "fnv1a",
lib/std/hash/siphash.zig deleted-393
......@@ -1,393 +0,0 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6// Siphash
7//
8// SipHash is a moderately fast, non-cryptographic keyed hash function designed for resistance
9// against hash flooding DoS attacks.
10//
11// https://131002.net/siphash/
12
13const std = @import("../std.zig");
14const assert = std.debug.assert;
15const testing = std.testing;
16const math = std.math;
17const mem = std.mem;
18
19const Endian = std.builtin.Endian;
20
21pub fn SipHash64(comptime c_rounds: usize, comptime d_rounds: usize) type {
22 return SipHash(u64, c_rounds, d_rounds);
23}
24
25pub fn SipHash128(comptime c_rounds: usize, comptime d_rounds: usize) type {
26 return SipHash(u128, c_rounds, d_rounds);
27}
28
29fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type {
30 assert(T == u64 or T == u128);
31 assert(c_rounds > 0 and d_rounds > 0);
32
33 return struct {
34 const Self = @This();
35 const digest_size = 64;
36 const block_size = 64;
37
38 v0: u64,
39 v1: u64,
40 v2: u64,
41 v3: u64,
42 msg_len: u8,
43
44 pub fn init(key: []const u8) Self {
45 assert(key.len >= 16);
46
47 const k0 = mem.readIntLittle(u64, key[0..8]);
48 const k1 = mem.readIntLittle(u64, key[8..16]);
49
50 var d = Self{
51 .v0 = k0 ^ 0x736f6d6570736575,
52 .v1 = k1 ^ 0x646f72616e646f6d,
53 .v2 = k0 ^ 0x6c7967656e657261,
54 .v3 = k1 ^ 0x7465646279746573,
55 .msg_len = 0,
56 };
57
58 if (T == u128) {
59 d.v1 ^= 0xee;
60 }
61
62 return d;
63 }
64
65 pub fn update(self: *Self, b: []const u8) void {
66 std.debug.assert(b.len % 8 == 0);
67
68 var off: usize = 0;
69 while (off < b.len) : (off += 8) {
70 @call(.{ .modifier = .always_inline }, self.round, .{b[off .. off + 8]});
71 }
72
73 self.msg_len +%= @truncate(u8, b.len);
74 }
75
76 pub fn final(self: *Self, b: []const u8) T {
77 std.debug.assert(b.len < 8);
78
79 self.msg_len +%= @truncate(u8, b.len);
80
81 var buf = [_]u8{0} ** 8;
82 mem.copy(u8, buf[0..], b[0..]);
83 buf[7] = self.msg_len;
84 self.round(buf[0..]);
85
86 if (T == u128) {
87 self.v2 ^= 0xee;
88 } else {
89 self.v2 ^= 0xff;
90 }
91
92 // TODO this is a workaround, should be able to supply the value without a separate variable
93 const inl = std.builtin.CallOptions{ .modifier = .always_inline };
94
95 comptime var i: usize = 0;
96 inline while (i < d_rounds) : (i += 1) {
97 @call(inl, sipRound, .{self});
98 }
99
100 const b1 = self.v0 ^ self.v1 ^ self.v2 ^ self.v3;
101 if (T == u64) {
102 return b1;
103 }
104
105 self.v1 ^= 0xdd;
106
107 comptime var j: usize = 0;
108 inline while (j < d_rounds) : (j += 1) {
109 @call(inl, sipRound, .{self});
110 }
111
112 const b2 = self.v0 ^ self.v1 ^ self.v2 ^ self.v3;
113 return (@as(u128, b2) << 64) | b1;
114 }
115
116 fn round(self: *Self, b: []const u8) void {
117 assert(b.len == 8);
118
119 const m = mem.readIntLittle(u64, b[0..8]);
120 self.v3 ^= m;
121
122 // TODO this is a workaround, should be able to supply the value without a separate variable
123 const inl = std.builtin.CallOptions{ .modifier = .always_inline };
124 comptime var i: usize = 0;
125 inline while (i < c_rounds) : (i += 1) {
126 @call(inl, sipRound, .{self});
127 }
128
129 self.v0 ^= m;
130 }
131
132 fn sipRound(d: *Self) void {
133 d.v0 +%= d.v1;
134 d.v1 = math.rotl(u64, d.v1, @as(u64, 13));
135 d.v1 ^= d.v0;
136 d.v0 = math.rotl(u64, d.v0, @as(u64, 32));
137 d.v2 +%= d.v3;
138 d.v3 = math.rotl(u64, d.v3, @as(u64, 16));
139 d.v3 ^= d.v2;
140 d.v0 +%= d.v3;
141 d.v3 = math.rotl(u64, d.v3, @as(u64, 21));
142 d.v3 ^= d.v0;
143 d.v2 +%= d.v1;
144 d.v1 = math.rotl(u64, d.v1, @as(u64, 17));
145 d.v1 ^= d.v2;
146 d.v2 = math.rotl(u64, d.v2, @as(u64, 32));
147 }
148
149 pub fn hash(key: []const u8, input: []const u8) T {
150 const aligned_len = input.len - (input.len % 8);
151
152 var c = Self.init(key);
153 @call(.{ .modifier = .always_inline }, c.update, .{input[0..aligned_len]});
154 return @call(.{ .modifier = .always_inline }, c.final, .{input[aligned_len..]});
155 }
156 };
157}
158
159pub fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type {
160 assert(T == u64 or T == u128);
161 assert(c_rounds > 0 and d_rounds > 0);
162
163 return struct {
164 const State = SipHashStateless(T, c_rounds, d_rounds);
165 const Self = @This();
166 const digest_size = 64;
167 const block_size = 64;
168
169 state: State,
170 buf: [8]u8,
171 buf_len: usize,
172
173 pub fn init(key: []const u8) Self {
174 return Self{
175 .state = State.init(key),
176 .buf = undefined,
177 .buf_len = 0,
178 };
179 }
180
181 pub fn update(self: *Self, b: []const u8) void {
182 var off: usize = 0;
183
184 if (self.buf_len != 0 and self.buf_len + b.len >= 8) {
185 off += 8 - self.buf_len;
186 mem.copy(u8, self.buf[self.buf_len..], b[0..off]);
187 self.state.update(self.buf[0..]);
188 self.buf_len = 0;
189 }
190
191 const remain_len = b.len - off;
192 const aligned_len = remain_len - (remain_len % 8);
193 self.state.update(b[off .. off + aligned_len]);
194
195 mem.copy(u8, self.buf[self.buf_len..], b[off + aligned_len ..]);
196 self.buf_len += @intCast(u8, b[off + aligned_len ..].len);
197 }
198
199 pub fn final(self: *Self) T {
200 return self.state.final(self.buf[0..self.buf_len]);
201 }
202
203 pub fn hash(key: []const u8, input: []const u8) T {
204 return State.hash(key, input);
205 }
206 };
207}
208
209// Test vectors from reference implementation.
210// https://github.com/veorq/SipHash/blob/master/vectors.h
211const test_key = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";
212
213test "siphash64-2-4 sanity" {
214 const vectors = [_][8]u8{
215 "\x31\x0e\x0e\xdd\x47\xdb\x6f\x72".*, // ""
216 "\xfd\x67\xdc\x93\xc5\x39\xf8\x74".*, // "\x00"
217 "\x5a\x4f\xa9\xd9\x09\x80\x6c\x0d".*, // "\x00\x01" ... etc
218 "\x2d\x7e\xfb\xd7\x96\x66\x67\x85".*,
219 "\xb7\x87\x71\x27\xe0\x94\x27\xcf".*,
220 "\x8d\xa6\x99\xcd\x64\x55\x76\x18".*,
221 "\xce\xe3\xfe\x58\x6e\x46\xc9\xcb".*,
222 "\x37\xd1\x01\x8b\xf5\x00\x02\xab".*,
223 "\x62\x24\x93\x9a\x79\xf5\xf5\x93".*,
224 "\xb0\xe4\xa9\x0b\xdf\x82\x00\x9e".*,
225 "\xf3\xb9\xdd\x94\xc5\xbb\x5d\x7a".*,
226 "\xa7\xad\x6b\x22\x46\x2f\xb3\xf4".*,
227 "\xfb\xe5\x0e\x86\xbc\x8f\x1e\x75".*,
228 "\x90\x3d\x84\xc0\x27\x56\xea\x14".*,
229 "\xee\xf2\x7a\x8e\x90\xca\x23\xf7".*,
230 "\xe5\x45\xbe\x49\x61\xca\x29\xa1".*,
231 "\xdb\x9b\xc2\x57\x7f\xcc\x2a\x3f".*,
232 "\x94\x47\xbe\x2c\xf5\xe9\x9a\x69".*,
233 "\x9c\xd3\x8d\x96\xf0\xb3\xc1\x4b".*,
234 "\xbd\x61\x79\xa7\x1d\xc9\x6d\xbb".*,
235 "\x98\xee\xa2\x1a\xf2\x5c\xd6\xbe".*,
236 "\xc7\x67\x3b\x2e\xb0\xcb\xf2\xd0".*,
237 "\x88\x3e\xa3\xe3\x95\x67\x53\x93".*,
238 "\xc8\xce\x5c\xcd\x8c\x03\x0c\xa8".*,
239 "\x94\xaf\x49\xf6\xc6\x50\xad\xb8".*,
240 "\xea\xb8\x85\x8a\xde\x92\xe1\xbc".*,
241 "\xf3\x15\xbb\x5b\xb8\x35\xd8\x17".*,
242 "\xad\xcf\x6b\x07\x63\x61\x2e\x2f".*,
243 "\xa5\xc9\x1d\xa7\xac\xaa\x4d\xde".*,
244 "\x71\x65\x95\x87\x66\x50\xa2\xa6".*,
245 "\x28\xef\x49\x5c\x53\xa3\x87\xad".*,
246 "\x42\xc3\x41\xd8\xfa\x92\xd8\x32".*,
247 "\xce\x7c\xf2\x72\x2f\x51\x27\x71".*,
248 "\xe3\x78\x59\xf9\x46\x23\xf3\xa7".*,
249 "\x38\x12\x05\xbb\x1a\xb0\xe0\x12".*,
250 "\xae\x97\xa1\x0f\xd4\x34\xe0\x15".*,
251 "\xb4\xa3\x15\x08\xbe\xff\x4d\x31".*,
252 "\x81\x39\x62\x29\xf0\x90\x79\x02".*,
253 "\x4d\x0c\xf4\x9e\xe5\xd4\xdc\xca".*,
254 "\x5c\x73\x33\x6a\x76\xd8\xbf\x9a".*,
255 "\xd0\xa7\x04\x53\x6b\xa9\x3e\x0e".*,
256 "\x92\x59\x58\xfc\xd6\x42\x0c\xad".*,
257 "\xa9\x15\xc2\x9b\xc8\x06\x73\x18".*,
258 "\x95\x2b\x79\xf3\xbc\x0a\xa6\xd4".*,
259 "\xf2\x1d\xf2\xe4\x1d\x45\x35\xf9".*,
260 "\x87\x57\x75\x19\x04\x8f\x53\xa9".*,
261 "\x10\xa5\x6c\xf5\xdf\xcd\x9a\xdb".*,
262 "\xeb\x75\x09\x5c\xcd\x98\x6c\xd0".*,
263 "\x51\xa9\xcb\x9e\xcb\xa3\x12\xe6".*,
264 "\x96\xaf\xad\xfc\x2c\xe6\x66\xc7".*,
265 "\x72\xfe\x52\x97\x5a\x43\x64\xee".*,
266 "\x5a\x16\x45\xb2\x76\xd5\x92\xa1".*,
267 "\xb2\x74\xcb\x8e\xbf\x87\x87\x0a".*,
268 "\x6f\x9b\xb4\x20\x3d\xe7\xb3\x81".*,
269 "\xea\xec\xb2\xa3\x0b\x22\xa8\x7f".*,
270 "\x99\x24\xa4\x3c\xc1\x31\x57\x24".*,
271 "\xbd\x83\x8d\x3a\xaf\xbf\x8d\xb7".*,
272 "\x0b\x1a\x2a\x32\x65\xd5\x1a\xea".*,
273 "\x13\x50\x79\xa3\x23\x1c\xe6\x60".*,
274 "\x93\x2b\x28\x46\xe4\xd7\x06\x66".*,
275 "\xe1\x91\x5f\x5c\xb1\xec\xa4\x6c".*,
276 "\xf3\x25\x96\x5c\xa1\x6d\x62\x9f".*,
277 "\x57\x5f\xf2\x8e\x60\x38\x1b\xe5".*,
278 "\x72\x45\x06\xeb\x4c\x32\x8a\x95".*,
279 };
280
281 const siphash = SipHash64(2, 4);
282
283 var buffer: [64]u8 = undefined;
284 for (vectors) |vector, i| {
285 buffer[i] = @intCast(u8, i);
286
287 const expected = mem.readIntLittle(u64, &vector);
288 testing.expectEqual(siphash.hash(test_key, buffer[0..i]), expected);
289 }
290}
291
292test "siphash128-2-4 sanity" {
293 const vectors = [_][16]u8{
294 "\xa3\x81\x7f\x04\xba\x25\xa8\xe6\x6d\xf6\x72\x14\xc7\x55\x02\x93".*,
295 "\xda\x87\xc1\xd8\x6b\x99\xaf\x44\x34\x76\x59\x11\x9b\x22\xfc\x45".*,
296 "\x81\x77\x22\x8d\xa4\xa4\x5d\xc7\xfc\xa3\x8b\xde\xf6\x0a\xff\xe4".*,
297 "\x9c\x70\xb6\x0c\x52\x67\xa9\x4e\x5f\x33\xb6\xb0\x29\x85\xed\x51".*,
298 "\xf8\x81\x64\xc1\x2d\x9c\x8f\xaf\x7d\x0f\x6e\x7c\x7b\xcd\x55\x79".*,
299 "\x13\x68\x87\x59\x80\x77\x6f\x88\x54\x52\x7a\x07\x69\x0e\x96\x27".*,
300 "\x14\xee\xca\x33\x8b\x20\x86\x13\x48\x5e\xa0\x30\x8f\xd7\xa1\x5e".*,
301 "\xa1\xf1\xeb\xbe\xd8\xdb\xc1\x53\xc0\xb8\x4a\xa6\x1f\xf0\x82\x39".*,
302 "\x3b\x62\xa9\xba\x62\x58\xf5\x61\x0f\x83\xe2\x64\xf3\x14\x97\xb4".*,
303 "\x26\x44\x99\x06\x0a\xd9\xba\xab\xc4\x7f\x8b\x02\xbb\x6d\x71\xed".*,
304 "\x00\x11\x0d\xc3\x78\x14\x69\x56\xc9\x54\x47\xd3\xf3\xd0\xfb\xba".*,
305 "\x01\x51\xc5\x68\x38\x6b\x66\x77\xa2\xb4\xdc\x6f\x81\xe5\xdc\x18".*,
306 "\xd6\x26\xb2\x66\x90\x5e\xf3\x58\x82\x63\x4d\xf6\x85\x32\xc1\x25".*,
307 "\x98\x69\xe2\x47\xe9\xc0\x8b\x10\xd0\x29\x93\x4f\xc4\xb9\x52\xf7".*,
308 "\x31\xfc\xef\xac\x66\xd7\xde\x9c\x7e\xc7\x48\x5f\xe4\x49\x49\x02".*,
309 "\x54\x93\xe9\x99\x33\xb0\xa8\x11\x7e\x08\xec\x0f\x97\xcf\xc3\xd9".*,
310 "\x6e\xe2\xa4\xca\x67\xb0\x54\xbb\xfd\x33\x15\xbf\x85\x23\x05\x77".*,
311 "\x47\x3d\x06\xe8\x73\x8d\xb8\x98\x54\xc0\x66\xc4\x7a\xe4\x77\x40".*,
312 "\xa4\x26\xe5\xe4\x23\xbf\x48\x85\x29\x4d\xa4\x81\xfe\xae\xf7\x23".*,
313 "\x78\x01\x77\x31\xcf\x65\xfa\xb0\x74\xd5\x20\x89\x52\x51\x2e\xb1".*,
314 "\x9e\x25\xfc\x83\x3f\x22\x90\x73\x3e\x93\x44\xa5\xe8\x38\x39\xeb".*,
315 "\x56\x8e\x49\x5a\xbe\x52\x5a\x21\x8a\x22\x14\xcd\x3e\x07\x1d\x12".*,
316 "\x4a\x29\xb5\x45\x52\xd1\x6b\x9a\x46\x9c\x10\x52\x8e\xff\x0a\xae".*,
317 "\xc9\xd1\x84\xdd\xd5\xa9\xf5\xe0\xcf\x8c\xe2\x9a\x9a\xbf\x69\x1c".*,
318 "\x2d\xb4\x79\xae\x78\xbd\x50\xd8\x88\x2a\x8a\x17\x8a\x61\x32\xad".*,
319 "\x8e\xce\x5f\x04\x2d\x5e\x44\x7b\x50\x51\xb9\xea\xcb\x8d\x8f\x6f".*,
320 "\x9c\x0b\x53\xb4\xb3\xc3\x07\xe8\x7e\xae\xe0\x86\x78\x14\x1f\x66".*,
321 "\xab\xf2\x48\xaf\x69\xa6\xea\xe4\xbf\xd3\xeb\x2f\x12\x9e\xeb\x94".*,
322 "\x06\x64\xda\x16\x68\x57\x4b\x88\xb9\x35\xf3\x02\x73\x58\xae\xf4".*,
323 "\xaa\x4b\x9d\xc4\xbf\x33\x7d\xe9\x0c\xd4\xfd\x3c\x46\x7c\x6a\xb7".*,
324 "\xea\x5c\x7f\x47\x1f\xaf\x6b\xde\x2b\x1a\xd7\xd4\x68\x6d\x22\x87".*,
325 "\x29\x39\xb0\x18\x32\x23\xfa\xfc\x17\x23\xde\x4f\x52\xc4\x3d\x35".*,
326 "\x7c\x39\x56\xca\x5e\xea\xfc\x3e\x36\x3e\x9d\x55\x65\x46\xeb\x68".*,
327 "\x77\xc6\x07\x71\x46\xf0\x1c\x32\xb6\xb6\x9d\x5f\x4e\xa9\xff\xcf".*,
328 "\x37\xa6\x98\x6c\xb8\x84\x7e\xdf\x09\x25\xf0\xf1\x30\x9b\x54\xde".*,
329 "\xa7\x05\xf0\xe6\x9d\xa9\xa8\xf9\x07\x24\x1a\x2e\x92\x3c\x8c\xc8".*,
330 "\x3d\xc4\x7d\x1f\x29\xc4\x48\x46\x1e\x9e\x76\xed\x90\x4f\x67\x11".*,
331 "\x0d\x62\xbf\x01\xe6\xfc\x0e\x1a\x0d\x3c\x47\x51\xc5\xd3\x69\x2b".*,
332 "\x8c\x03\x46\x8b\xca\x7c\x66\x9e\xe4\xfd\x5e\x08\x4b\xbe\xe7\xb5".*,
333 "\x52\x8a\x5b\xb9\x3b\xaf\x2c\x9c\x44\x73\xcc\xe5\xd0\xd2\x2b\xd9".*,
334 "\xdf\x6a\x30\x1e\x95\xc9\x5d\xad\x97\xae\x0c\xc8\xc6\x91\x3b\xd8".*,
335 "\x80\x11\x89\x90\x2c\x85\x7f\x39\xe7\x35\x91\x28\x5e\x70\xb6\xdb".*,
336 "\xe6\x17\x34\x6a\xc9\xc2\x31\xbb\x36\x50\xae\x34\xcc\xca\x0c\x5b".*,
337 "\x27\xd9\x34\x37\xef\xb7\x21\xaa\x40\x18\x21\xdc\xec\x5a\xdf\x89".*,
338 "\x89\x23\x7d\x9d\xed\x9c\x5e\x78\xd8\xb1\xc9\xb1\x66\xcc\x73\x42".*,
339 "\x4a\x6d\x80\x91\xbf\x5e\x7d\x65\x11\x89\xfa\x94\xa2\x50\xb1\x4c".*,
340 "\x0e\x33\xf9\x60\x55\xe7\xae\x89\x3f\xfc\x0e\x3d\xcf\x49\x29\x02".*,
341 "\xe6\x1c\x43\x2b\x72\x0b\x19\xd1\x8e\xc8\xd8\x4b\xdc\x63\x15\x1b".*,
342 "\xf7\xe5\xae\xf5\x49\xf7\x82\xcf\x37\x90\x55\xa6\x08\x26\x9b\x16".*,
343 "\x43\x8d\x03\x0f\xd0\xb7\xa5\x4f\xa8\x37\xf2\xad\x20\x1a\x64\x03".*,
344 "\xa5\x90\xd3\xee\x4f\xbf\x04\xe3\x24\x7e\x0d\x27\xf2\x86\x42\x3f".*,
345 "\x5f\xe2\xc1\xa1\x72\xfe\x93\xc4\xb1\x5c\xd3\x7c\xae\xf9\xf5\x38".*,
346 "\x2c\x97\x32\x5c\xbd\x06\xb3\x6e\xb2\x13\x3d\xd0\x8b\x3a\x01\x7c".*,
347 "\x92\xc8\x14\x22\x7a\x6b\xca\x94\x9f\xf0\x65\x9f\x00\x2a\xd3\x9e".*,
348 "\xdc\xe8\x50\x11\x0b\xd8\x32\x8c\xfb\xd5\x08\x41\xd6\x91\x1d\x87".*,
349 "\x67\xf1\x49\x84\xc7\xda\x79\x12\x48\xe3\x2b\xb5\x92\x25\x83\xda".*,
350 "\x19\x38\xf2\xcf\x72\xd5\x4e\xe9\x7e\x94\x16\x6f\xa9\x1d\x2a\x36".*,
351 "\x74\x48\x1e\x96\x46\xed\x49\xfe\x0f\x62\x24\x30\x16\x04\x69\x8e".*,
352 "\x57\xfc\xa5\xde\x98\xa9\xd6\xd8\x00\x64\x38\xd0\x58\x3d\x8a\x1d".*,
353 "\x9f\xec\xde\x1c\xef\xdc\x1c\xbe\xd4\x76\x36\x74\xd9\x57\x53\x59".*,
354 "\xe3\x04\x0c\x00\xeb\x28\xf1\x53\x66\xca\x73\xcb\xd8\x72\xe7\x40".*,
355 "\x76\x97\x00\x9a\x6a\x83\x1d\xfe\xcc\xa9\x1c\x59\x93\x67\x0f\x7a".*,
356 "\x58\x53\x54\x23\x21\xf5\x67\xa0\x05\xd5\x47\xa4\xf0\x47\x59\xbd".*,
357 "\x51\x50\xd1\x77\x2f\x50\x83\x4a\x50\x3e\x06\x9a\x97\x3f\xbd\x7c".*,
358 };
359
360 const siphash = SipHash128(2, 4);
361
362 var buffer: [64]u8 = undefined;
363 for (vectors) |vector, i| {
364 buffer[i] = @intCast(u8, i);
365
366 const expected = mem.readIntLittle(u128, &vector);
367 testing.expectEqual(siphash.hash(test_key, buffer[0..i]), expected);
368 }
369}
370
371test "iterative non-divisible update" {
372 var buf: [1024]u8 = undefined;
373 for (buf) |*e, i| {
374 e.* = @truncate(u8, i);
375 }
376
377 const key = "0x128dad08f12307";
378 const Siphash = SipHash64(2, 4);
379
380 var end: usize = 9;
381 while (end < buf.len) : (end += 9) {
382 const non_iterative_hash = Siphash.hash(key, buf[0..end]);
383
384 var wy = Siphash.init(key);
385 var i: usize = 0;
386 while (i < end) : (i += 7) {
387 wy.update(buf[i..std.math.min(i + 7, end)]);
388 }
389 const iterative_hash = wy.final();
390
391 std.testing.expectEqual(iterative_hash, non_iterative_hash);
392 }
393}