1const std = @import("../std.zig");
2const mem = std.mem;
3const math = std.math;
4const debug = std.debug;
5const htest = @import("test.zig");
6
7const RoundParam = struct {
8 a: usize,
9 b: usize,
10 c: usize,
11 d: usize,
12 x: usize,
13 y: usize,
14};
15
16fn roundParam(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) RoundParam {
17 return RoundParam{
18 .a = a,
19 .b = b,
20 .c = c,
21 .d = d,
22 .x = x,
23 .y = y,
24 };
25}
26
27/////////////////////
28// Blake2s
29
30pub const Blake2s128 = Blake2s(128);
31pub const Blake2s160 = Blake2s(160);
32pub const Blake2s224 = Blake2s(224);
33pub const Blake2s256 = Blake2s(256);
34
35pub fn Blake2s(comptime out_bits: usize) type {
36 return struct {
37 const Self = @This();
38 pub const block_length = 64;
39 pub const digest_length = out_bits / 8;
40 pub const key_length_min = 0;
41 pub const key_length_max = 32;
42 pub const key_length = 32; // recommended key length
43 pub const Options = struct { key: ?[]const u8 = null, salt: ?[8]u8 = null, context: ?[8]u8 = null, expected_out_bits: usize = out_bits };
44
45 const iv = [8]u32{
46 0x6A09E667,
47 0xBB67AE85,
48 0x3C6EF372,
49 0xA54FF53A,
50 0x510E527F,
51 0x9B05688C,
52 0x1F83D9AB,
53 0x5BE0CD19,
54 };
55
56 const sigma = [10][16]u8{
57 [_]u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
58 [_]u8{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
59 [_]u8{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
60 [_]u8{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
61 [_]u8{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
62 [_]u8{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
63 [_]u8{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
64 [_]u8{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
65 [_]u8{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
66 [_]u8{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
67 };
68
69 h: [8]u32,
70 t: u64,
71 // Streaming cache
72 buf: [64]u8,
73 buf_len: u8,
74
75 pub fn init(options: Options) Self {
76 comptime debug.assert(8 <= out_bits and out_bits <= 256);
77
78 var d: Self = undefined;
79 d.h = iv;
80
81 const key_len = if (options.key) |key| key.len else 0;
82 // default parameters
83 d.h[0] ^= 0x01010000 ^ @as(u32, @truncate(key_len << 8)) ^ @as(u32, @intCast(options.expected_out_bits >> 3));
84 d.t = 0;
85 d.buf_len = 0;
86
87 if (options.salt) |salt| {
88 d.h[4] ^= mem.readInt(u32, salt[0..4], .little);
89 d.h[5] ^= mem.readInt(u32, salt[4..8], .little);
90 }
91 if (options.context) |context| {
92 d.h[6] ^= mem.readInt(u32, context[0..4], .little);
93 d.h[7] ^= mem.readInt(u32, context[4..8], .little);
94 }
95 if (key_len > 0) {
96 @memset(d.buf[key_len..], 0);
97 d.update(options.key.?);
98 d.buf_len = 64;
99 }
100 return d;
101 }
102
103 pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {
104 var d = Self.init(options);
105 d.update(b);
106 d.final(out);
107 }
108
109 pub fn update(d: *Self, b: []const u8) void {
110 var off: usize = 0;
111
112 // Partial buffer exists from previous update. Copy into buffer then hash.
113 if (d.buf_len != 0 and d.buf_len + b.len > 64) {
114 off += 64 - d.buf_len;
115 @memcpy(d.buf[d.buf_len..][0..off], b[0..off]);
116 d.t += 64;
117 d.round(d.buf[0..], false);
118 d.buf_len = 0;
119 }
120
121 // Full middle blocks.
122 while (off + 64 < b.len) : (off += 64) {
123 d.t += 64;
124 d.round(b[off..][0..64], false);
125 }
126
127 // Copy any remainder for next pass.
128 const b_slice = b[off..];
129 @memcpy(d.buf[d.buf_len..][0..b_slice.len], b_slice);
130 d.buf_len += @as(u8, @intCast(b_slice.len));
131 }
132
133 pub fn final(d: *Self, out: *[digest_length]u8) void {
134 @memset(d.buf[d.buf_len..], 0);
135 d.t += d.buf_len;
136 d.round(d.buf[0..], true);
137 for (&d.h) |*x| x.* = mem.nativeToLittle(u32, x.*);
138 out.* = @as(*[digest_length]u8, @ptrCast(&d.h)).*;
139 }
140
141 fn round(d: *Self, b: *const [64]u8, last: bool) void {
142 var m: [16]u32 = undefined;
143 var v: [16]u32 = undefined;
144
145 for (&m, 0..) |*r, i| {
146 r.* = mem.readInt(u32, b[4 * i ..][0..4], .little);
147 }
148
149 var k: usize = 0;
150 while (k < 8) : (k += 1) {
151 v[k] = d.h[k];
152 v[k + 8] = iv[k];
153 }
154
155 v[12] ^= @as(u32, @truncate(d.t));
156 v[13] ^= @as(u32, @intCast(d.t >> 32));
157 if (last) v[14] = ~v[14];
158
159 const rounds = comptime [_]RoundParam{
160 roundParam(0, 4, 8, 12, 0, 1),
161 roundParam(1, 5, 9, 13, 2, 3),
162 roundParam(2, 6, 10, 14, 4, 5),
163 roundParam(3, 7, 11, 15, 6, 7),
164 roundParam(0, 5, 10, 15, 8, 9),
165 roundParam(1, 6, 11, 12, 10, 11),
166 roundParam(2, 7, 8, 13, 12, 13),
167 roundParam(3, 4, 9, 14, 14, 15),
168 };
169
170 comptime var j: usize = 0;
171 inline while (j < 10) : (j += 1) {
172 inline for (rounds) |r| {
173 v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]];
174 v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], @as(usize, 16));
175 v[r.c] = v[r.c] +% v[r.d];
176 v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], @as(usize, 12));
177 v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]];
178 v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], @as(usize, 8));
179 v[r.c] = v[r.c] +% v[r.d];
180 v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], @as(usize, 7));
181 }
182 }
183
184 for (&d.h, 0..) |*r, i| {
185 r.* ^= v[i] ^ v[i + 8];
186 }
187 }
188 };
189}
190
191test "blake2s160 single" {
192 const h1 = "354c9c33f735962418bdacb9479873429c34916f";
193 try htest.assertEqualHash(Blake2s160, h1, "");
194
195 const h2 = "5ae3b99be29b01834c3b508521ede60438f8de17";
196 try htest.assertEqualHash(Blake2s160, h2, "abc");
197
198 const h3 = "5a604fec9713c369e84b0ed68daed7d7504ef240";
199 try htest.assertEqualHash(Blake2s160, h3, "The quick brown fox jumps over the lazy dog");
200
201 const h4 = "b60c4dc60e2681e58fbc24e77f07e02c69e72ed0";
202 const repeat_a_32: [32]u8 = @splat('a');
203 const repeat_b_32: [32]u8 = @splat('b');
204 try htest.assertEqualHash(Blake2s160, h4, &repeat_a_32 ++ &repeat_b_32);
205}
206
207test "blake2s160 streaming" {
208 var h = Blake2s160.init(.{});
209 var out: [20]u8 = undefined;
210
211 const h1 = "354c9c33f735962418bdacb9479873429c34916f";
212
213 h.final(out[0..]);
214 try htest.assertEqual(h1, out[0..]);
215
216 const h2 = "5ae3b99be29b01834c3b508521ede60438f8de17";
217
218 h = Blake2s160.init(.{});
219 h.update("abc");
220 h.final(out[0..]);
221 try htest.assertEqual(h2, out[0..]);
222
223 h = Blake2s160.init(.{});
224 h.update("a");
225 h.update("b");
226 h.update("c");
227 h.final(out[0..]);
228 try htest.assertEqual(h2, out[0..]);
229
230 const h3 = "b60c4dc60e2681e58fbc24e77f07e02c69e72ed0";
231
232 const repeat_a_32: [32]u8 = @splat('a');
233 const repeat_b_32: [32]u8 = @splat('b');
234
235 h = Blake2s160.init(.{});
236 h.update(&repeat_a_32);
237 h.update(&repeat_b_32);
238 h.final(out[0..]);
239 try htest.assertEqual(h3, out[0..]);
240
241 h = Blake2s160.init(.{});
242 h.update(&repeat_a_32 ++ &repeat_b_32);
243 h.final(out[0..]);
244 try htest.assertEqual(h3, out[0..]);
245
246 const h4 = "4667fd60791a7fe41f939bca646b4529e296bd68";
247
248 h = Blake2s160.init(.{ .context = @splat(0x69), .salt = @splat(0x42) });
249 h.update(&repeat_a_32);
250 h.update(&repeat_b_32);
251 h.final(out[0..]);
252 try htest.assertEqual(h4, out[0..]);
253
254 h = Blake2s160.init(.{ .context = @splat(0x69), .salt = @splat(0x42) });
255 h.update(&repeat_a_32 ++ &repeat_b_32);
256 h.final(out[0..]);
257 try htest.assertEqual(h4, out[0..]);
258}
259
260test "comptime blake2s160" {
261 //comptime
262 {
263 @setEvalBranchQuota(10000);
264 var block: [Blake2s160.block_length]u8 = @splat(0);
265 var out: [Blake2s160.digest_length]u8 = undefined;
266
267 const h1 = "2c56ad9d0b2c8b474aafa93ab307db2f0940105f";
268
269 try htest.assertEqualHash(Blake2s160, h1, block[0..]);
270
271 var h = Blake2s160.init(.{});
272 h.update(&block);
273 h.final(out[0..]);
274
275 try htest.assertEqual(h1, out[0..]);
276 }
277}
278
279test "blake2s224 single" {
280 const h1 = "1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4";
281 try htest.assertEqualHash(Blake2s224, h1, "");
282
283 const h2 = "0b033fc226df7abde29f67a05d3dc62cf271ef3dfea4d387407fbd55";
284 try htest.assertEqualHash(Blake2s224, h2, "abc");
285
286 const h3 = "e4e5cb6c7cae41982b397bf7b7d2d9d1949823ae78435326e8db4912";
287 try htest.assertEqualHash(Blake2s224, h3, "The quick brown fox jumps over the lazy dog");
288
289 const h4 = "557381a78facd2b298640f4e32113e58967d61420af1aa939d0cfe01";
290 const repeat_a_32: [32]u8 = @splat('a');
291 const repeat_b_32: [32]u8 = @splat('b');
292 try htest.assertEqualHash(Blake2s224, h4, &repeat_a_32 ++ &repeat_b_32);
293}
294
295test "blake2s224 streaming" {
296 var h = Blake2s224.init(.{});
297 var out: [28]u8 = undefined;
298
299 const h1 = "1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4";
300
301 h.final(out[0..]);
302 try htest.assertEqual(h1, out[0..]);
303
304 const h2 = "0b033fc226df7abde29f67a05d3dc62cf271ef3dfea4d387407fbd55";
305
306 h = Blake2s224.init(.{});
307 h.update("abc");
308 h.final(out[0..]);
309 try htest.assertEqual(h2, out[0..]);
310
311 h = Blake2s224.init(.{});
312 h.update("a");
313 h.update("b");
314 h.update("c");
315 h.final(out[0..]);
316 try htest.assertEqual(h2, out[0..]);
317
318 const repeat_a_32: [32]u8 = @splat('a');
319 const repeat_b_32: [32]u8 = @splat('b');
320
321 const h3 = "557381a78facd2b298640f4e32113e58967d61420af1aa939d0cfe01";
322
323 h = Blake2s224.init(.{});
324 h.update(&repeat_a_32);
325 h.update(&repeat_b_32);
326 h.final(out[0..]);
327 try htest.assertEqual(h3, out[0..]);
328
329 h = Blake2s224.init(.{});
330 h.update(&repeat_a_32 ++ &repeat_b_32);
331 h.final(out[0..]);
332 try htest.assertEqual(h3, out[0..]);
333
334 const h4 = "a4d6a9d253441b80e5dfd60a04db169ffab77aec56a2855c402828c3";
335
336 h = Blake2s224.init(.{ .context = @splat(0x69), .salt = @splat(0x42) });
337 h.update(&repeat_a_32);
338 h.update(&repeat_b_32);
339 h.final(out[0..]);
340 try htest.assertEqual(h4, out[0..]);
341
342 h = Blake2s224.init(.{ .context = @splat(0x69), .salt = @splat(0x42) });
343 h.update(&repeat_a_32 ++ &repeat_b_32);
344 h.final(out[0..]);
345 try htest.assertEqual(h4, out[0..]);
346}
347
348test "comptime blake2s224" {
349 comptime {
350 @setEvalBranchQuota(10000);
351 var block: [Blake2s224.block_length]u8 = @splat(0);
352 var out: [Blake2s224.digest_length]u8 = undefined;
353
354 const h1 = "86b7611563293f8c73627df7a6d6ba25ca0548c2a6481f7d116ee576";
355
356 try htest.assertEqualHash(Blake2s224, h1, block[0..]);
357
358 var h = Blake2s224.init(.{});
359 h.update(&block);
360 h.final(out[0..]);
361
362 try htest.assertEqual(h1, out[0..]);
363 }
364}
365
366test "blake2s256 single" {
367 const h1 = "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9";
368 try htest.assertEqualHash(Blake2s256, h1, "");
369
370 const h2 = "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982";
371 try htest.assertEqualHash(Blake2s256, h2, "abc");
372
373 const h3 = "606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812";
374 try htest.assertEqualHash(Blake2s256, h3, "The quick brown fox jumps over the lazy dog");
375
376 const h4 = "8d8711dade07a6b92b9a3ea1f40bee9b2c53ff3edd2a273dec170b0163568977";
377 const repeat_a_32: [32]u8 = @splat('a');
378 const repeat_b_32: [32]u8 = @splat('b');
379 try htest.assertEqualHash(Blake2s256, h4, &repeat_a_32 ++ &repeat_b_32);
380}
381
382test "blake2s256 streaming" {
383 var h = Blake2s256.init(.{});
384 var out: [32]u8 = undefined;
385
386 const h1 = "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9";
387
388 h.final(out[0..]);
389 try htest.assertEqual(h1, out[0..]);
390
391 const h2 = "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982";
392
393 h = Blake2s256.init(.{});
394 h.update("abc");
395 h.final(out[0..]);
396 try htest.assertEqual(h2, out[0..]);
397
398 h = Blake2s256.init(.{});
399 h.update("a");
400 h.update("b");
401 h.update("c");
402 h.final(out[0..]);
403 try htest.assertEqual(h2, out[0..]);
404
405 const repeat_a_32: [32]u8 = @splat('a');
406 const repeat_b_32: [32]u8 = @splat('b');
407
408 const h3 = "8d8711dade07a6b92b9a3ea1f40bee9b2c53ff3edd2a273dec170b0163568977";
409
410 h = Blake2s256.init(.{});
411 h.update(&repeat_a_32);
412 h.update(&repeat_b_32);
413 h.final(out[0..]);
414 try htest.assertEqual(h3, out[0..]);
415
416 h = Blake2s256.init(.{});
417 h.update(&repeat_a_32 ++ &repeat_b_32);
418 h.final(out[0..]);
419 try htest.assertEqual(h3, out[0..]);
420}
421
422test "blake2s256 keyed" {
423 var out: [32]u8 = undefined;
424
425 const h1 = "10f918da4d74fab3302e48a5d67d03804b1ec95372a62a0f33b7c9fa28ba1ae6";
426 const key = "secret_key";
427
428 const repeat_a_64: [64]u8 = @splat('a');
429 const repeat_b_64: [64]u8 = @splat('b');
430
431 Blake2s256.hash(&repeat_a_64 ++ &repeat_b_64, &out, .{ .key = key });
432 try htest.assertEqual(h1, out[0..]);
433
434 var h = Blake2s256.init(.{ .key = key });
435 h.update(&repeat_a_64 ++ &repeat_b_64);
436 h.final(out[0..]);
437
438 try htest.assertEqual(h1, out[0..]);
439
440 h = Blake2s256.init(.{ .key = key });
441 h.update(&repeat_a_64);
442 h.update(&repeat_b_64);
443 h.final(out[0..]);
444
445 try htest.assertEqual(h1, out[0..]);
446}
447
448test "comptime blake2s256" {
449 comptime {
450 @setEvalBranchQuota(10000);
451 var block: [Blake2s256.block_length]u8 = @splat(0);
452 var out: [Blake2s256.digest_length]u8 = undefined;
453
454 const h1 = "ae09db7cd54f42b490ef09b6bc541af688e4959bb8c53f359a6f56e38ab454a3";
455
456 try htest.assertEqualHash(Blake2s256, h1, block[0..]);
457
458 var h = Blake2s256.init(.{});
459 h.update(&block);
460 h.final(out[0..]);
461
462 try htest.assertEqual(h1, out[0..]);
463 }
464}
465
466/////////////////////
467// Blake2b
468
469pub const Blake2b128 = Blake2b(128);
470pub const Blake2b160 = Blake2b(160);
471pub const Blake2b256 = Blake2b(256);
472pub const Blake2b384 = Blake2b(384);
473pub const Blake2b512 = Blake2b(512);
474
475pub fn Blake2b(comptime out_bits: usize) type {
476 return struct {
477 const Self = @This();
478 pub const block_length = 128;
479 pub const digest_length = out_bits / 8;
480 pub const key_length_min = 0;
481 pub const key_length_max = 64;
482 pub const key_length = 32; // recommended key length
483 pub const Options = struct { key: ?[]const u8 = null, salt: ?[16]u8 = null, context: ?[16]u8 = null, expected_out_bits: usize = out_bits };
484
485 const iv = [8]u64{
486 0x6a09e667f3bcc908,
487 0xbb67ae8584caa73b,
488 0x3c6ef372fe94f82b,
489 0xa54ff53a5f1d36f1,
490 0x510e527fade682d1,
491 0x9b05688c2b3e6c1f,
492 0x1f83d9abfb41bd6b,
493 0x5be0cd19137e2179,
494 };
495
496 const sigma = [12][16]u8{
497 [_]u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
498 [_]u8{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
499 [_]u8{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
500 [_]u8{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
501 [_]u8{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
502 [_]u8{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
503 [_]u8{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
504 [_]u8{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
505 [_]u8{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
506 [_]u8{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
507 [_]u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
508 [_]u8{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
509 };
510
511 h: [8]u64,
512 t: u128,
513 // Streaming cache
514 buf: [128]u8,
515 buf_len: u8,
516
517 pub fn init(options: Options) Self {
518 comptime debug.assert(8 <= out_bits and out_bits <= 512);
519
520 var d: Self = undefined;
521 d.h = iv;
522
523 const key_len = if (options.key) |key| key.len else 0;
524 // default parameters
525 d.h[0] ^= 0x01010000 ^ (key_len << 8) ^ (options.expected_out_bits >> 3);
526 d.t = 0;
527 d.buf_len = 0;
528
529 if (options.salt) |salt| {
530 d.h[4] ^= mem.readInt(u64, salt[0..8], .little);
531 d.h[5] ^= mem.readInt(u64, salt[8..16], .little);
532 }
533 if (options.context) |context| {
534 d.h[6] ^= mem.readInt(u64, context[0..8], .little);
535 d.h[7] ^= mem.readInt(u64, context[8..16], .little);
536 }
537 if (key_len > 0) {
538 @memset(d.buf[key_len..], 0);
539 d.update(options.key.?);
540 d.buf_len = 128;
541 }
542 return d;
543 }
544
545 pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {
546 var d = Self.init(options);
547 d.update(b);
548 d.final(out);
549 }
550
551 pub fn update(d: *Self, b: []const u8) void {
552 var off: usize = 0;
553
554 // Partial buffer exists from previous update. Copy into buffer then hash.
555 if (d.buf_len != 0 and d.buf_len + b.len > 128) {
556 off += 128 - d.buf_len;
557 @memcpy(d.buf[d.buf_len..][0..off], b[0..off]);
558 d.t += 128;
559 d.round(d.buf[0..], false);
560 d.buf_len = 0;
561 }
562
563 // Full middle blocks.
564 while (off + 128 < b.len) : (off += 128) {
565 d.t += 128;
566 d.round(b[off..][0..128], false);
567 }
568
569 // Copy any remainder for next pass.
570 const b_slice = b[off..];
571 @memcpy(d.buf[d.buf_len..][0..b_slice.len], b_slice);
572 d.buf_len += @as(u8, @intCast(b_slice.len));
573 }
574
575 pub fn final(d: *Self, out: *[digest_length]u8) void {
576 @memset(d.buf[d.buf_len..], 0);
577 d.t += d.buf_len;
578 d.round(d.buf[0..], true);
579 for (&d.h) |*x| x.* = mem.nativeToLittle(u64, x.*);
580 out.* = @as(*[digest_length]u8, @ptrCast(&d.h)).*;
581 }
582
583 fn round(d: *Self, b: *const [128]u8, last: bool) void {
584 var m: [16]u64 = undefined;
585 var v: [16]u64 = undefined;
586
587 for (&m, 0..) |*r, i| {
588 r.* = mem.readInt(u64, b[8 * i ..][0..8], .little);
589 }
590
591 var k: usize = 0;
592 while (k < 8) : (k += 1) {
593 v[k] = d.h[k];
594 v[k + 8] = iv[k];
595 }
596
597 v[12] ^= @as(u64, @truncate(d.t));
598 v[13] ^= @as(u64, @intCast(d.t >> 64));
599 if (last) v[14] = ~v[14];
600
601 const rounds = comptime [_]RoundParam{
602 roundParam(0, 4, 8, 12, 0, 1),
603 roundParam(1, 5, 9, 13, 2, 3),
604 roundParam(2, 6, 10, 14, 4, 5),
605 roundParam(3, 7, 11, 15, 6, 7),
606 roundParam(0, 5, 10, 15, 8, 9),
607 roundParam(1, 6, 11, 12, 10, 11),
608 roundParam(2, 7, 8, 13, 12, 13),
609 roundParam(3, 4, 9, 14, 14, 15),
610 };
611
612 comptime var j: usize = 0;
613 inline while (j < 12) : (j += 1) {
614 inline for (rounds) |r| {
615 v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]];
616 v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], @as(usize, 32));
617 v[r.c] = v[r.c] +% v[r.d];
618 v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], @as(usize, 24));
619 v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]];
620 v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], @as(usize, 16));
621 v[r.c] = v[r.c] +% v[r.d];
622 v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], @as(usize, 63));
623 }
624 }
625
626 for (&d.h, 0..) |*r, i| {
627 r.* ^= v[i] ^ v[i + 8];
628 }
629 }
630 };
631}
632
633test "blake2b160 single" {
634 const h1 = "3345524abf6bbe1809449224b5972c41790b6cf2";
635 try htest.assertEqualHash(Blake2b160, h1, "");
636
637 const h2 = "384264f676f39536840523f284921cdc68b6846b";
638 try htest.assertEqualHash(Blake2b160, h2, "abc");
639
640 const h3 = "3c523ed102ab45a37d54f5610d5a983162fde84f";
641 try htest.assertEqualHash(Blake2b160, h3, "The quick brown fox jumps over the lazy dog");
642
643 const h4 = "43758f5de1740f651f1ae39de92260fe8bd5a11f";
644 const repeat_a_64: [64]u8 = @splat('a');
645 const repeat_b_64: [64]u8 = @splat('b');
646 try htest.assertEqualHash(Blake2b160, h4, &repeat_a_64 ++ &repeat_b_64);
647}
648
649test "blake2b160 streaming" {
650 var h = Blake2b160.init(.{});
651 var out: [20]u8 = undefined;
652
653 const h1 = "3345524abf6bbe1809449224b5972c41790b6cf2";
654
655 h.final(out[0..]);
656 try htest.assertEqual(h1, out[0..]);
657
658 const h2 = "384264f676f39536840523f284921cdc68b6846b";
659
660 h = Blake2b160.init(.{});
661 h.update("abc");
662 h.final(out[0..]);
663 try htest.assertEqual(h2, out[0..]);
664
665 h = Blake2b160.init(.{});
666 h.update("a");
667 h.update("b");
668 h.update("c");
669 h.final(out[0..]);
670 try htest.assertEqual(h2, out[0..]);
671
672 const repeat_a_64: [64]u8 = @splat('a');
673 const repeat_b_64: [64]u8 = @splat('b');
674
675 const h3 = "43758f5de1740f651f1ae39de92260fe8bd5a11f";
676
677 h = Blake2b160.init(.{});
678 h.update(&repeat_a_64 ++ &repeat_b_64);
679 h.final(out[0..]);
680 try htest.assertEqual(h3, out[0..]);
681
682 h = Blake2b160.init(.{});
683 h.update(&repeat_a_64);
684 h.update(&repeat_b_64);
685 h.final(out[0..]);
686 try htest.assertEqual(h3, out[0..]);
687
688 h = Blake2b160.init(.{});
689 h.update(&repeat_a_64);
690 h.update(&repeat_b_64);
691 h.final(out[0..]);
692 try htest.assertEqual(h3, out[0..]);
693
694 const h4 = "72328f8a8200663752fc302d372b5dd9b49dd8dc";
695
696 h = Blake2b160.init(.{ .context = @splat(0x69), .salt = @splat(0x42) });
697 h.update(&repeat_a_64);
698 h.update(&repeat_b_64);
699 h.final(out[0..]);
700 try htest.assertEqual(h4, out[0..]);
701
702 h = Blake2b160.init(.{ .context = @splat(0x69), .salt = @splat(0x42) });
703 h.update(&repeat_a_64);
704 h.update(&repeat_b_64);
705 h.final(out[0..]);
706 try htest.assertEqual(h4, out[0..]);
707}
708
709test "comptime blake2b160" {
710 comptime {
711 @setEvalBranchQuota(10000);
712 var block: [Blake2b160.block_length]u8 = @splat(0);
713 var out: [Blake2b160.digest_length]u8 = undefined;
714
715 const h1 = "8d26f158f564e3293b42f5e3d34263cb173aa9c9";
716
717 try htest.assertEqualHash(Blake2b160, h1, block[0..]);
718
719 var h = Blake2b160.init(.{});
720 h.update(&block);
721 h.final(out[0..]);
722
723 try htest.assertEqual(h1, out[0..]);
724 }
725}
726
727test "blake2b384 single" {
728 const h1 = "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100";
729 try htest.assertEqualHash(Blake2b384, h1, "");
730
731 const h2 = "6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4";
732 try htest.assertEqualHash(Blake2b384, h2, "abc");
733
734 const h3 = "b7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d";
735 try htest.assertEqualHash(Blake2b384, h3, "The quick brown fox jumps over the lazy dog");
736
737 const h4 = "b7283f0172fecbbd7eca32ce10d8a6c06b453cb3cf675b33eb4246f0da2bb94a6c0bdd6eec0b5fd71ec4fd51be80bf4c";
738 const repeat_a_64: [64]u8 = @splat('a');
739 const repeat_b_64: [64]u8 = @splat('b');
740 try htest.assertEqualHash(Blake2b384, h4, &repeat_a_64 ++ &repeat_b_64);
741}
742
743test "blake2b384 streaming" {
744 var h = Blake2b384.init(.{});
745 var out: [48]u8 = undefined;
746
747 const h1 = "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100";
748
749 h.final(out[0..]);
750 try htest.assertEqual(h1, out[0..]);
751
752 const h2 = "6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4";
753
754 h = Blake2b384.init(.{});
755 h.update("abc");
756 h.final(out[0..]);
757 try htest.assertEqual(h2, out[0..]);
758
759 h = Blake2b384.init(.{});
760 h.update("a");
761 h.update("b");
762 h.update("c");
763 h.final(out[0..]);
764 try htest.assertEqual(h2, out[0..]);
765
766 const repeat_a_64: [64]u8 = @splat('a');
767 const repeat_b_64: [64]u8 = @splat('b');
768
769 const h3 = "b7283f0172fecbbd7eca32ce10d8a6c06b453cb3cf675b33eb4246f0da2bb94a6c0bdd6eec0b5fd71ec4fd51be80bf4c";
770
771 h = Blake2b384.init(.{});
772 h.update(&repeat_a_64 ++ &repeat_b_64);
773 h.final(out[0..]);
774 try htest.assertEqual(h3, out[0..]);
775
776 h = Blake2b384.init(.{});
777 h.update(&repeat_a_64);
778 h.update(&repeat_b_64);
779 h.final(out[0..]);
780 try htest.assertEqual(h3, out[0..]);
781
782 h = Blake2b384.init(.{});
783 h.update(&repeat_a_64);
784 h.update(&repeat_b_64);
785 h.final(out[0..]);
786 try htest.assertEqual(h3, out[0..]);
787
788 const h4 = "934c48fcb197031c71f583d92f98703510805e72142e0b46f5752d1e971bc86c355d556035613ff7a4154b4de09dac5c";
789
790 h = Blake2b384.init(.{ .context = @splat(0x69), .salt = @splat(0x42) });
791 h.update(&repeat_a_64);
792 h.update(&repeat_b_64);
793 h.final(out[0..]);
794 try htest.assertEqual(h4, out[0..]);
795
796 h = Blake2b384.init(.{ .context = @splat(0x69), .salt = @splat(0x42) });
797 h.update(&repeat_a_64);
798 h.update(&repeat_b_64);
799 h.final(out[0..]);
800 try htest.assertEqual(h4, out[0..]);
801}
802
803test "comptime blake2b384" {
804 comptime {
805 @setEvalBranchQuota(20000);
806 var block: [Blake2b384.block_length]u8 = @splat(0);
807 var out: [Blake2b384.digest_length]u8 = undefined;
808
809 const h1 = "e8aa1931ea0422e4446fecdd25c16cf35c240b10cb4659dd5c776eddcaa4d922397a589404b46eb2e53d78132d05fd7d";
810
811 try htest.assertEqualHash(Blake2b384, h1, block[0..]);
812
813 var h = Blake2b384.init(.{});
814 h.update(&block);
815 h.final(out[0..]);
816
817 try htest.assertEqual(h1, out[0..]);
818 }
819}
820
821test "blake2b512 single" {
822 const h1 = "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce";
823 try htest.assertEqualHash(Blake2b512, h1, "");
824
825 const h2 = "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923";
826 try htest.assertEqualHash(Blake2b512, h2, "abc");
827
828 const h3 = "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918";
829 try htest.assertEqualHash(Blake2b512, h3, "The quick brown fox jumps over the lazy dog");
830
831 const h4 = "049980af04d6a2cf16b4b49793c3ed7e40732073788806f2c989ebe9547bda0541d63abe298ec8955d08af48ae731f2e8a0bd6d201655a5473b4aa79d211b920";
832 const repeat_a_64: [64]u8 = @splat('a');
833 const repeat_b_64: [64]u8 = @splat('b');
834 try htest.assertEqualHash(Blake2b512, h4, &repeat_a_64 ++ &repeat_b_64);
835}
836
837test "blake2b512 streaming" {
838 var h = Blake2b512.init(.{});
839 var out: [64]u8 = undefined;
840
841 const h1 = "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce";
842
843 h.final(out[0..]);
844 try htest.assertEqual(h1, out[0..]);
845
846 const h2 = "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923";
847
848 h = Blake2b512.init(.{});
849 h.update("abc");
850 h.final(out[0..]);
851 try htest.assertEqual(h2, out[0..]);
852
853 h = Blake2b512.init(.{});
854 h.update("a");
855 h.update("b");
856 h.update("c");
857 h.final(out[0..]);
858 try htest.assertEqual(h2, out[0..]);
859
860 const repeat_a_64: [64]u8 = @splat('a');
861 const repeat_b_64: [64]u8 = @splat('b');
862
863 const h3 = "049980af04d6a2cf16b4b49793c3ed7e40732073788806f2c989ebe9547bda0541d63abe298ec8955d08af48ae731f2e8a0bd6d201655a5473b4aa79d211b920";
864
865 h = Blake2b512.init(.{});
866 h.update(&repeat_a_64 ++ &repeat_b_64);
867 h.final(out[0..]);
868 try htest.assertEqual(h3, out[0..]);
869
870 h = Blake2b512.init(.{});
871 h.update(&repeat_a_64);
872 h.update(&repeat_b_64);
873 h.final(out[0..]);
874 try htest.assertEqual(h3, out[0..]);
875}
876
877test "blake2b512 keyed" {
878 var out: [64]u8 = undefined;
879
880 const h1 = "8a978060ccaf582f388f37454363071ac9a67e3a704585fd879fb8a419a447e389c7c6de790faa20a7a7dccf197de736bc5b40b98a930b36df5bee7555750c4d";
881 const key = "secret_key";
882
883 const repeat_a_64: [64]u8 = @splat('a');
884 const repeat_b_64: [64]u8 = @splat('b');
885
886 Blake2b512.hash(&repeat_a_64 ++ &repeat_b_64, &out, .{ .key = key });
887 try htest.assertEqual(h1, out[0..]);
888
889 var h = Blake2b512.init(.{ .key = key });
890 h.update(&repeat_a_64 ++ &repeat_b_64);
891 h.final(out[0..]);
892
893 try htest.assertEqual(h1, out[0..]);
894
895 h = Blake2b512.init(.{ .key = key });
896 h.update(&repeat_a_64);
897 h.update(&repeat_b_64);
898 h.final(out[0..]);
899
900 try htest.assertEqual(h1, out[0..]);
901}
902
903test "comptime blake2b512" {
904 comptime {
905 @setEvalBranchQuota(12000);
906 var block: [Blake2b512.block_length]u8 = @splat(0);
907 var out: [Blake2b512.digest_length]u8 = undefined;
908
909 const h1 = "865939e120e6805438478841afb739ae4250cf372653078a065cdcfffca4caf798e6d462b65d658fc165782640eded70963449ae1500fb0f24981d7727e22c41";
910
911 try htest.assertEqualHash(Blake2b512, h1, block[0..]);
912
913 var h = Blake2b512.init(.{});
914 h.update(&block);
915 h.final(out[0..]);
916
917 try htest.assertEqual(h1, out[0..]);
918 }
919}