authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2018-01-15 23:14:13+13:00
committergravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2018-01-15 23:14:13+13:00
log4cf86b4a94adf0a27d0c536746f1703ddf1fd0df
tree654ddb0b439983d2ee010f552bbfaf387b0d0538
parente7e7625633d4d6ef41b6f9cbf2f02d49945a9cda

Add Blake2X hash functions

The truncated output variants currently are dependent on a more complete bigint implementation in the compiler.

3 files changed, 447 insertions(+), 0 deletions(-)

CMakeLists.txt+1
...@@ -368,6 +368,7 @@ set(ZIG_STD_FILES...@@ -368,6 +368,7 @@ set(ZIG_STD_FILES
368 "crypto/md5.zig"368 "crypto/md5.zig"
369 "crypto/sha1.zig"369 "crypto/sha1.zig"
370 "crypto/sha2.zig"370 "crypto/sha2.zig"
371 "crypto/blake2x.zig"
371 "cstr.zig"372 "cstr.zig"
372 "debug/failing_allocator.zig"373 "debug/failing_allocator.zig"
373 "debug/index.zig"374 "debug/index.zig"
std/crypto/blake2x.zig created+439
...@@ -0,0 +1,439 @@
1const std = @import("std");
2const mem = std.mem;
3const math = std.math;
4const debug = std.debug;
5
6const RoundParam = struct {
7 a: usize, b: usize, c: usize, d: usize, x: usize, y: usize,
8};
9
10fn Rp(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) -> RoundParam {
11 return RoundParam { .a = a, .b = b, .c = c, .d = d, .x = x, .y = y, };
12}
13
14/////////////////////
15// Blake2s
16
17pub const Blake2s224 = Blake2s(224);
18pub const Blake2s256 = Blake2s(256);
19
20fn Blake2s(comptime out_len: usize) -> type { return struct {
21 const Self = this;
22 const ReturnType = @IntType(false, out_len);
23
24 const iv = [8]u32 {
25 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
26 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
27 };
28
29 const sigma = [10][16]u8 {
30 []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
31 []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
32 []const u8 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
33 []const u8 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
34 []const u8 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
35 []const u8 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
36 []const u8 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
37 []const u8 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
38 []const u8 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
39 []const u8 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
40 };
41
42 h: [8]u32,
43 t: u64,
44 // Streaming cache
45 buf: [64]u8,
46 buf_len: u8,
47
48 pub fn init() -> Self {
49 debug.assert(8 <= out_len and out_len <= 512);
50
51 var s: Self = undefined;
52 s.reset();
53 return s;
54 }
55
56 pub fn reset(d: &Self) {
57 mem.copy(u32, d.h[0..], iv[0..]);
58
59 // No key plus default parameters
60 d.h[0] ^= 0x01010000 ^ u32(out_len >> 3);
61 d.t = 0;
62 d.buf_len = 0;
63 }
64
65 pub fn hash(b: []const u8) -> ReturnType {
66 var d = Self.init();
67 d.update(b);
68 return d.final();
69 }
70
71 pub fn update(d: &Self, b: []const u8) {
72 var off: usize = 0;
73
74 // Partial buffer exists from previous update. Copy into buffer then hash.
75 if (d.buf_len != 0 and d.buf_len + b.len > 64) {
76 off += 64 - d.buf_len;
77 mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
78 d.t += 64;
79 d.round(d.buf[0..], false);
80 d.buf_len = 0;
81 }
82
83 // Full middle blocks.
84 while (off + 64 < b.len) : (off += 64) {
85 d.t += 64;
86 d.round(b[off..off + 64], false);
87 }
88
89 // Copy any remainder for next pass.
90 mem.copy(u8, d.buf[d.buf_len..], b[off..]);
91 d.buf_len += u8(b[off..].len);
92 }
93
94 pub fn final(d: &Self) -> ReturnType {
95 mem.set(u8, d.buf[d.buf_len..], 0);
96 d.t += d.buf_len;
97 d.round(d.buf[0..], true);
98
99 const rr = d.h[0 .. out_len / 32];
100
101 // NOTE: mem.readIntLE or equivalent would be useful here.
102 var j: u8 = 0;
103 var r: ReturnType = 0;
104 for (rr) |p| {
105 r |= ReturnType(p) << j;
106 j +%= 32;
107 }
108
109 return std.endian.swapIfLe(ReturnType, r);
110 }
111
112 fn round(d: &Self, b: []const u8, last: bool) {
113 debug.assert(b.len == 64);
114
115 var m: [16]u32 = undefined;
116 var v: [16]u32 = undefined;
117
118 for (m) |*r, i| {
119 *r = mem.readIntLE(u32, b[4*i .. 4*i + 4]);
120 }
121
122 var k: usize = 0;
123 while (k < 8) : (k += 1) {
124 v[k] = d.h[k];
125 v[k+8] = iv[k];
126 }
127
128 v[12] ^= @truncate(u32, d.t);
129 v[13] ^= u32(d.t >> 32);
130 if (last) v[14] = ~v[14];
131
132 const rounds = comptime []RoundParam {
133 Rp(0, 4, 8, 12, 0, 1),
134 Rp(1, 5, 9, 13, 2, 3),
135 Rp(2, 6, 10, 14, 4, 5),
136 Rp(3, 7, 11, 15, 6, 7),
137 Rp(0, 5, 10, 15, 8, 9),
138 Rp(1, 6, 11, 12, 10, 11),
139 Rp(2, 7, 8, 13, 12, 13),
140 Rp(3, 4, 9, 14, 14, 15),
141 };
142
143 comptime var j: usize = 0;
144 inline while (j < 10) : (j += 1) {
145 inline for (rounds) |r| {
146 v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]];
147 v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(16));
148 v[r.c] = v[r.c] +% v[r.d];
149 v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(12));
150 v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]];
151 v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(8));
152 v[r.c] = v[r.c] +% v[r.d];
153 v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(7));
154 }
155 }
156
157 for (d.h) |*r, i| {
158 *r ^= v[i] ^ v[i + 8];
159 }
160 }
161};}
162
163// TODO: bigint rem >1 digits for 224 integer output.
164//
165// test "blake2s224 single" {
166// const hash1 = 0xa847d26c2f966c5c4cc222b174918a56037cdee34b3f872f;
167// debug.assert(hash1 == Blake2s224.hash(""));
168//
169// const hash2 = 0x1e2ed10fcdbc46e0ab3ea3f268a6c288083ae04e3d63a8de;
170// debug.assert(hash2 == Blake2s224.hash("abc"));
171//
172// const hash3 = 0xe486adf7b22d2944b434ae78ae64720c16ccf0479dab072d;
173// debug.assert(hash3 == Blake2s224.hash("The quick brown fox jumps over the lazy dog"));
174// }
175//
176// test "blake2s224 streaming" {
177// var h = Blake2s224.init();
178//
179// const hash1 = 0xa847d26c2f966c5c4cc222b174918a56037cdee34b3f872f;
180// debug.assert(hash1 == h.final());
181//
182// const hash2 = 0x1e2ed10fcdbc46e0ab3ea3f268a6c288083ae04e3d63a8de;
183//
184// h.reset();
185// h.update("abc");
186// debug.assert(hash2 == h.final());
187//
188// h.reset();
189// h.update("a");
190// h.update("b");
191// h.update("c");
192// debug.assert(hash2 == h.final());
193// }
194
195test "blake2s256 single" {
196 const hash1 = 0x69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9;
197 debug.assert(hash1 == Blake2s256.hash(""));
198
199 const hash2 = 0x508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982;
200 debug.assert(hash2 == Blake2s256.hash("abc"));
201
202 const hash3 = 0x606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812;
203 debug.assert(hash3 == Blake2s256.hash("The quick brown fox jumps over the lazy dog"));
204}
205
206test "blake2s256 streaming" {
207 var h = Blake2s256.init();
208
209 const hash1 = 0x69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9;
210 debug.assert(hash1 == h.final());
211
212 const hash2 = 0x508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982;
213
214 h.reset();
215 h.update("abc");
216 debug.assert(hash2 == h.final());
217
218 h.reset();
219 h.update("a");
220 h.update("b");
221 h.update("c");
222 debug.assert(hash2 == h.final());
223}
224
225
226/////////////////////
227// Blake2b
228
229pub const Blake2b384 = Blake2b(384);
230pub const Blake2b512 = Blake2b(512);
231
232fn Blake2b(comptime out_len: usize) -> type { return struct {
233 const Self = this;
234 const ReturnType = @IntType(false, out_len);
235 const u9 = @IntType(false, 9);
236
237 const iv = [8]u64 {
238 0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
239 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
240 0x510e527fade682d1, 0x9b05688c2b3e6c1f,
241 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
242 };
243
244 const sigma = [12][16]u8 {
245 []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
246 []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
247 []const u8 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
248 []const u8 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
249 []const u8 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
250 []const u8 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
251 []const u8 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
252 []const u8 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
253 []const u8 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
254 []const u8 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 },
255 []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
256 []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
257 };
258
259 h: [8]u64,
260 t: u128,
261 // Streaming cache
262 buf: [128]u8,
263 buf_len: u8,
264
265 pub fn init() -> Self {
266 debug.assert(8 <= out_len and out_len <= 512);
267
268 var s: Self = undefined;
269 s.reset();
270 return s;
271 }
272
273 pub fn reset(d: &Self) {
274 mem.copy(u64, d.h[0..], iv[0..]);
275
276 // No key plus default parameters
277 d.h[0] ^= 0x01010000 ^ (out_len >> 3);
278 d.t = 0;
279 d.buf_len = 0;
280 }
281
282 pub fn hash(b: []const u8) -> ReturnType {
283 var d = Self.init();
284 d.update(b);
285 return d.final();
286 }
287
288 pub fn update(d: &Self, b: []const u8) {
289 var off: usize = 0;
290
291 // Partial buffer exists from previous update. Copy into buffer then hash.
292 if (d.buf_len != 0 and d.buf_len + b.len > 128) {
293 off += 128 - d.buf_len;
294 mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
295 d.t += 128;
296 d.round(d.buf[0..], false);
297 d.buf_len = 0;
298 }
299
300 // Full middle blocks.
301 while (off + 128 < b.len) : (off += 128) {
302 d.t += 128;
303 d.round(b[off..off + 128], false);
304 }
305
306 // Copy any remainder for next pass.
307 mem.copy(u8, d.buf[d.buf_len..], b[off..]);
308 d.buf_len += u8(b[off..].len);
309 }
310
311 pub fn final(d: &Self) -> ReturnType {
312 mem.set(u8, d.buf[d.buf_len..], 0);
313 d.t += d.buf_len;
314 d.round(d.buf[0..], true);
315
316 const rr = d.h[0 .. out_len / 64];
317
318 var j: u9 = 0;
319 var r: ReturnType = 0;
320 for (rr) |p| {
321 r |= ReturnType(p) << j;
322 j +%= 64;
323 }
324
325 return std.endian.swapIfLe(ReturnType, r);
326 }
327
328 fn round(d: &Self, b: []const u8, last: bool) {
329 debug.assert(b.len == 128);
330
331 var m: [16]u64 = undefined;
332 var v: [16]u64 = undefined;
333
334 for (m) |*r, i| {
335 *r = mem.readIntLE(u64, b[8*i .. 8*i + 8]);
336 }
337
338 var k: usize = 0;
339 while (k < 8) : (k += 1) {
340 v[k] = d.h[k];
341 v[k+8] = iv[k];
342 }
343
344 v[12] ^= @truncate(u64, d.t);
345 v[13] ^= u64(d.t >> 64);
346 if (last) v[14] = ~v[14];
347
348 const rounds = comptime []RoundParam {
349 Rp(0, 4, 8, 12, 0, 1),
350 Rp(1, 5, 9, 13, 2, 3),
351 Rp(2, 6, 10, 14, 4, 5),
352 Rp(3, 7, 11, 15, 6, 7),
353 Rp(0, 5, 10, 15, 8, 9),
354 Rp(1, 6, 11, 12, 10, 11),
355 Rp(2, 7, 8, 13, 12, 13),
356 Rp(3, 4, 9, 14, 14, 15),
357 };
358
359 comptime var j: usize = 0;
360 inline while (j < 12) : (j += 1) {
361 inline for (rounds) |r| {
362 v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]];
363 v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(32));
364 v[r.c] = v[r.c] +% v[r.d];
365 v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(24));
366 v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]];
367 v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(16));
368 v[r.c] = v[r.c] +% v[r.d];
369 v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(63));
370 }
371 }
372
373 for (d.h) |*r, i| {
374 *r ^= v[i] ^ v[i + 8];
375 }
376 }
377};}
378
379// TODO: bigint rem >1 digits for 384 integer output.
380
381// test "blake2b384 single" {
382// const hash1 = 0xb32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100;
383// debug.assert(hash1 == Blake2b384.hash(""));
384//
385// const hash2 = 0x6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4;
386// debug.assert(hash2 == Blake2b384.hash("abc"));
387//
388// const hash3 = 0xb7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d;
389// debug.assert(hash3 == Blake2b384.hash("The quick brown fox jumps over the lazy dog"));
390// }
391//
392// test "blake2b384 streaming" {
393// var h = Blake2b384.init();
394//
395// const hash1 = 0xb32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100;
396// debug.assert(hash1 == h.final());
397//
398// const hash2 = 0x6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4;
399//
400// h.reset();
401// h.update("abc");
402// debug.assert(hash2 == h.final());
403//
404// h.reset();
405// h.update("a");
406// h.update("b");
407// h.update("c");
408// debug.assert(hash2 == h.final());
409// }
410
411test "blake2b512 single" {
412 const hash1 = 0x786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce;
413 debug.assert(hash1 == Blake2b512.hash(""));
414
415 const hash2 = 0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923;
416 debug.assert(hash2 == Blake2b512.hash("abc"));
417
418 const hash3 = 0xa8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918;
419 debug.assert(hash3 == Blake2b512.hash("The quick brown fox jumps over the lazy dog"));
420}
421
422test "blake2b512 streaming" {
423 var h = Blake2b512.init();
424
425 const hash1 = 0x786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce;
426 debug.assert(hash1 == h.final());
427
428 const hash2 = 0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923;
429
430 h.reset();
431 h.update("abc");
432 debug.assert(hash2 == h.final());
433
434 h.reset();
435 h.update("a");
436 h.update("b");
437 h.update("c");
438 debug.assert(hash2 == h.final());
439}
std/crypto/index.zig+7
...@@ -7,8 +7,15 @@ pub const Sha256 = sha2.Sha256;...@@ -7,8 +7,15 @@ pub const Sha256 = sha2.Sha256;
7pub const Sha384 = sha2.Sha384;7pub const Sha384 = sha2.Sha384;
8pub const Sha512 = sha2.Sha512;8pub const Sha512 = sha2.Sha512;
99
10const blake2x = @import("blake2x.zig");
11pub const Blake2s224 = blake2x.Blake2s224;
12pub const Blake2s256 = blake2x.Blake2s256;
13pub const Blake2b384 = blake2x.Blake2b384;
14pub const Blake2b512 = blake2x.Blake2b512;
15
10test "crypto" {16test "crypto" {
11 _ = @import("md5.zig");17 _ = @import("md5.zig");
12 _ = @import("sha1.zig");18 _ = @import("sha1.zig");
13 _ = @import("sha2.zig");19 _ = @import("sha2.zig");
20 _ = @import("blake2x.zig");
14}21}