From 4cf86b4a94adf0a27d0c536746f1703ddf1fd0df Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Mon, 15 Jan 2018 23:14:13 +1300 Subject: [PATCH 1/3] Add Blake2X hash functions The truncated output variants currently are dependent on a more complete bigint implementation in the compiler. --- CMakeLists.txt | 1 + std/crypto/blake2x.zig | 439 +++++++++++++++++++++++++++++++++++++++++ std/crypto/index.zig | 7 + 3 files changed, 447 insertions(+) create mode 100644 std/crypto/blake2x.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 636c215f063e..b93951e4e620 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -368,6 +368,7 @@ set(ZIG_STD_FILES "crypto/md5.zig" "crypto/sha1.zig" "crypto/sha2.zig" + "crypto/blake2x.zig" "cstr.zig" "debug/failing_allocator.zig" "debug/index.zig" diff --git a/std/crypto/blake2x.zig b/std/crypto/blake2x.zig new file mode 100644 index 000000000000..67ef00610b23 --- /dev/null +++ b/std/crypto/blake2x.zig @@ -0,0 +1,439 @@ +const std = @import("std"); +const mem = std.mem; +const math = std.math; +const debug = std.debug; + +const RoundParam = struct { + a: usize, b: usize, c: usize, d: usize, x: usize, y: usize, +}; + +fn Rp(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) -> RoundParam { + return RoundParam { .a = a, .b = b, .c = c, .d = d, .x = x, .y = y, }; +} + +///////////////////// +// Blake2s + +pub const Blake2s224 = Blake2s(224); +pub const Blake2s256 = Blake2s(256); + +fn Blake2s(comptime out_len: usize) -> type { return struct { + const Self = this; + const ReturnType = @IntType(false, out_len); + + const iv = [8]u32 { + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19, + }; + + const sigma = [10][16]u8 { + []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + []const u8 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + []const u8 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + []const u8 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + []const u8 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + []const u8 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, + []const u8 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + []const u8 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, + []const u8 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, + }; + + h: [8]u32, + t: u64, + // Streaming cache + buf: [64]u8, + buf_len: u8, + + pub fn init() -> Self { + debug.assert(8 <= out_len and out_len <= 512); + + var s: Self = undefined; + s.reset(); + return s; + } + + pub fn reset(d: &Self) { + mem.copy(u32, d.h[0..], iv[0..]); + + // No key plus default parameters + d.h[0] ^= 0x01010000 ^ u32(out_len >> 3); + d.t = 0; + d.buf_len = 0; + } + + pub fn hash(b: []const u8) -> ReturnType { + var d = Self.init(); + d.update(b); + return d.final(); + } + + pub fn update(d: &Self, b: []const u8) { + var off: usize = 0; + + // Partial buffer exists from previous update. Copy into buffer then hash. + if (d.buf_len != 0 and d.buf_len + b.len > 64) { + off += 64 - d.buf_len; + mem.copy(u8, d.buf[d.buf_len..], b[0..off]); + d.t += 64; + d.round(d.buf[0..], false); + d.buf_len = 0; + } + + // Full middle blocks. + while (off + 64 < b.len) : (off += 64) { + d.t += 64; + d.round(b[off..off + 64], false); + } + + // Copy any remainder for next pass. + mem.copy(u8, d.buf[d.buf_len..], b[off..]); + d.buf_len += u8(b[off..].len); + } + + pub fn final(d: &Self) -> ReturnType { + mem.set(u8, d.buf[d.buf_len..], 0); + d.t += d.buf_len; + d.round(d.buf[0..], true); + + const rr = d.h[0 .. out_len / 32]; + + // NOTE: mem.readIntLE or equivalent would be useful here. + var j: u8 = 0; + var r: ReturnType = 0; + for (rr) |p| { + r |= ReturnType(p) << j; + j +%= 32; + } + + return std.endian.swapIfLe(ReturnType, r); + } + + fn round(d: &Self, b: []const u8, last: bool) { + debug.assert(b.len == 64); + + var m: [16]u32 = undefined; + var v: [16]u32 = undefined; + + for (m) |*r, i| { + *r = mem.readIntLE(u32, b[4*i .. 4*i + 4]); + } + + var k: usize = 0; + while (k < 8) : (k += 1) { + v[k] = d.h[k]; + v[k+8] = iv[k]; + } + + v[12] ^= @truncate(u32, d.t); + v[13] ^= u32(d.t >> 32); + if (last) v[14] = ~v[14]; + + const rounds = comptime []RoundParam { + Rp(0, 4, 8, 12, 0, 1), + Rp(1, 5, 9, 13, 2, 3), + Rp(2, 6, 10, 14, 4, 5), + Rp(3, 7, 11, 15, 6, 7), + Rp(0, 5, 10, 15, 8, 9), + Rp(1, 6, 11, 12, 10, 11), + Rp(2, 7, 8, 13, 12, 13), + Rp(3, 4, 9, 14, 14, 15), + }; + + comptime var j: usize = 0; + inline while (j < 10) : (j += 1) { + inline for (rounds) |r| { + v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]]; + v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(16)); + v[r.c] = v[r.c] +% v[r.d]; + v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(12)); + v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]]; + v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(8)); + v[r.c] = v[r.c] +% v[r.d]; + v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(7)); + } + } + + for (d.h) |*r, i| { + *r ^= v[i] ^ v[i + 8]; + } + } +};} + +// TODO: bigint rem >1 digits for 224 integer output. +// +// test "blake2s224 single" { +// const hash1 = 0xa847d26c2f966c5c4cc222b174918a56037cdee34b3f872f; +// debug.assert(hash1 == Blake2s224.hash("")); +// +// const hash2 = 0x1e2ed10fcdbc46e0ab3ea3f268a6c288083ae04e3d63a8de; +// debug.assert(hash2 == Blake2s224.hash("abc")); +// +// const hash3 = 0xe486adf7b22d2944b434ae78ae64720c16ccf0479dab072d; +// debug.assert(hash3 == Blake2s224.hash("The quick brown fox jumps over the lazy dog")); +// } +// +// test "blake2s224 streaming" { +// var h = Blake2s224.init(); +// +// const hash1 = 0xa847d26c2f966c5c4cc222b174918a56037cdee34b3f872f; +// debug.assert(hash1 == h.final()); +// +// const hash2 = 0x1e2ed10fcdbc46e0ab3ea3f268a6c288083ae04e3d63a8de; +// +// h.reset(); +// h.update("abc"); +// debug.assert(hash2 == h.final()); +// +// h.reset(); +// h.update("a"); +// h.update("b"); +// h.update("c"); +// debug.assert(hash2 == h.final()); +// } + +test "blake2s256 single" { + const hash1 = 0x69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9; + debug.assert(hash1 == Blake2s256.hash("")); + + const hash2 = 0x508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982; + debug.assert(hash2 == Blake2s256.hash("abc")); + + const hash3 = 0x606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812; + debug.assert(hash3 == Blake2s256.hash("The quick brown fox jumps over the lazy dog")); +} + +test "blake2s256 streaming" { + var h = Blake2s256.init(); + + const hash1 = 0x69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9; + debug.assert(hash1 == h.final()); + + const hash2 = 0x508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982; + + h.reset(); + h.update("abc"); + debug.assert(hash2 == h.final()); + + h.reset(); + h.update("a"); + h.update("b"); + h.update("c"); + debug.assert(hash2 == h.final()); +} + + +///////////////////// +// Blake2b + +pub const Blake2b384 = Blake2b(384); +pub const Blake2b512 = Blake2b(512); + +fn Blake2b(comptime out_len: usize) -> type { return struct { + const Self = this; + const ReturnType = @IntType(false, out_len); + const u9 = @IntType(false, 9); + + const iv = [8]u64 { + 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179, + }; + + const sigma = [12][16]u8 { + []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + []const u8 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + []const u8 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + []const u8 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + []const u8 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + []const u8 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, + []const u8 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + []const u8 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, + []const u8 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 }, + []const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + []const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + }; + + h: [8]u64, + t: u128, + // Streaming cache + buf: [128]u8, + buf_len: u8, + + pub fn init() -> Self { + debug.assert(8 <= out_len and out_len <= 512); + + var s: Self = undefined; + s.reset(); + return s; + } + + pub fn reset(d: &Self) { + mem.copy(u64, d.h[0..], iv[0..]); + + // No key plus default parameters + d.h[0] ^= 0x01010000 ^ (out_len >> 3); + d.t = 0; + d.buf_len = 0; + } + + pub fn hash(b: []const u8) -> ReturnType { + var d = Self.init(); + d.update(b); + return d.final(); + } + + pub fn update(d: &Self, b: []const u8) { + var off: usize = 0; + + // Partial buffer exists from previous update. Copy into buffer then hash. + if (d.buf_len != 0 and d.buf_len + b.len > 128) { + off += 128 - d.buf_len; + mem.copy(u8, d.buf[d.buf_len..], b[0..off]); + d.t += 128; + d.round(d.buf[0..], false); + d.buf_len = 0; + } + + // Full middle blocks. + while (off + 128 < b.len) : (off += 128) { + d.t += 128; + d.round(b[off..off + 128], false); + } + + // Copy any remainder for next pass. + mem.copy(u8, d.buf[d.buf_len..], b[off..]); + d.buf_len += u8(b[off..].len); + } + + pub fn final(d: &Self) -> ReturnType { + mem.set(u8, d.buf[d.buf_len..], 0); + d.t += d.buf_len; + d.round(d.buf[0..], true); + + const rr = d.h[0 .. out_len / 64]; + + var j: u9 = 0; + var r: ReturnType = 0; + for (rr) |p| { + r |= ReturnType(p) << j; + j +%= 64; + } + + return std.endian.swapIfLe(ReturnType, r); + } + + fn round(d: &Self, b: []const u8, last: bool) { + debug.assert(b.len == 128); + + var m: [16]u64 = undefined; + var v: [16]u64 = undefined; + + for (m) |*r, i| { + *r = mem.readIntLE(u64, b[8*i .. 8*i + 8]); + } + + var k: usize = 0; + while (k < 8) : (k += 1) { + v[k] = d.h[k]; + v[k+8] = iv[k]; + } + + v[12] ^= @truncate(u64, d.t); + v[13] ^= u64(d.t >> 64); + if (last) v[14] = ~v[14]; + + const rounds = comptime []RoundParam { + Rp(0, 4, 8, 12, 0, 1), + Rp(1, 5, 9, 13, 2, 3), + Rp(2, 6, 10, 14, 4, 5), + Rp(3, 7, 11, 15, 6, 7), + Rp(0, 5, 10, 15, 8, 9), + Rp(1, 6, 11, 12, 10, 11), + Rp(2, 7, 8, 13, 12, 13), + Rp(3, 4, 9, 14, 14, 15), + }; + + comptime var j: usize = 0; + inline while (j < 12) : (j += 1) { + inline for (rounds) |r| { + v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]]; + v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(32)); + v[r.c] = v[r.c] +% v[r.d]; + v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(24)); + v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]]; + v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(16)); + v[r.c] = v[r.c] +% v[r.d]; + v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(63)); + } + } + + for (d.h) |*r, i| { + *r ^= v[i] ^ v[i + 8]; + } + } +};} + +// TODO: bigint rem >1 digits for 384 integer output. + +// test "blake2b384 single" { +// const hash1 = 0xb32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100; +// debug.assert(hash1 == Blake2b384.hash("")); +// +// const hash2 = 0x6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4; +// debug.assert(hash2 == Blake2b384.hash("abc")); +// +// const hash3 = 0xb7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d; +// debug.assert(hash3 == Blake2b384.hash("The quick brown fox jumps over the lazy dog")); +// } +// +// test "blake2b384 streaming" { +// var h = Blake2b384.init(); +// +// const hash1 = 0xb32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100; +// debug.assert(hash1 == h.final()); +// +// const hash2 = 0x6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4; +// +// h.reset(); +// h.update("abc"); +// debug.assert(hash2 == h.final()); +// +// h.reset(); +// h.update("a"); +// h.update("b"); +// h.update("c"); +// debug.assert(hash2 == h.final()); +// } + +test "blake2b512 single" { + const hash1 = 0x786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce; + debug.assert(hash1 == Blake2b512.hash("")); + + const hash2 = 0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923; + debug.assert(hash2 == Blake2b512.hash("abc")); + + const hash3 = 0xa8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918; + debug.assert(hash3 == Blake2b512.hash("The quick brown fox jumps over the lazy dog")); +} + +test "blake2b512 streaming" { + var h = Blake2b512.init(); + + const hash1 = 0x786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce; + debug.assert(hash1 == h.final()); + + const hash2 = 0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923; + + h.reset(); + h.update("abc"); + debug.assert(hash2 == h.final()); + + h.reset(); + h.update("a"); + h.update("b"); + h.update("c"); + debug.assert(hash2 == h.final()); +} diff --git a/std/crypto/index.zig b/std/crypto/index.zig index 27dd467bd0db..a63984e3d5dc 100644 --- a/std/crypto/index.zig +++ b/std/crypto/index.zig @@ -7,8 +7,15 @@ pub const Sha256 = sha2.Sha256; pub const Sha384 = sha2.Sha384; pub const Sha512 = sha2.Sha512; +const blake2x = @import("blake2x.zig"); +pub const Blake2s224 = blake2x.Blake2s224; +pub const Blake2s256 = blake2x.Blake2s256; +pub const Blake2b384 = blake2x.Blake2b384; +pub const Blake2b512 = blake2x.Blake2b512; + test "crypto" { _ = @import("md5.zig"); _ = @import("sha1.zig"); _ = @import("sha2.zig"); + _ = @import("blake2x.zig"); } From fa7b33549e43abbdb4fba1d686dfd63c2ca8e8ca Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Tue, 16 Jan 2018 21:35:31 +1300 Subject: [PATCH 2/3] Change crypto functions to fill a buffer - Rename blake2x -> blake2 - Fix blake2s truncated tests --- CMakeLists.txt | 2 +- std/crypto/{blake2x.zig => blake2.zig} | 230 +++++++++++++------------ std/crypto/index.zig | 12 +- std/crypto/md5.zig | 47 ++--- std/crypto/sha1.zig | 38 ++-- std/crypto/sha2.zig | 121 +++++++------ 6 files changed, 238 insertions(+), 212 deletions(-) rename std/crypto/{blake2x.zig => blake2.zig} (65%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b93951e4e620..8a090d9f2918 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -368,7 +368,7 @@ set(ZIG_STD_FILES "crypto/md5.zig" "crypto/sha1.zig" "crypto/sha2.zig" - "crypto/blake2x.zig" + "crypto/blake2.zig" "cstr.zig" "debug/failing_allocator.zig" "debug/index.zig" diff --git a/std/crypto/blake2x.zig b/std/crypto/blake2.zig similarity index 65% rename from std/crypto/blake2x.zig rename to std/crypto/blake2.zig index 67ef00610b23..b08caa480eb4 100644 --- a/std/crypto/blake2x.zig +++ b/std/crypto/blake2.zig @@ -1,7 +1,9 @@ -const std = @import("std"); -const mem = std.mem; -const math = std.math; -const debug = std.debug; +const mem = @import("../mem.zig"); +const math = @import("../math/index.zig"); +const endian = @import("../endian.zig"); +const debug = @import("../debug/index.zig"); +const builtin = @import("builtin"); +const htest = @import("test.zig"); const RoundParam = struct { a: usize, b: usize, c: usize, d: usize, x: usize, y: usize, @@ -19,7 +21,6 @@ pub const Blake2s256 = Blake2s(256); fn Blake2s(comptime out_len: usize) -> type { return struct { const Self = this; - const ReturnType = @IntType(false, out_len); const iv = [8]u32 { 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, @@ -62,10 +63,10 @@ fn Blake2s(comptime out_len: usize) -> type { return struct { d.buf_len = 0; } - pub fn hash(b: []const u8) -> ReturnType { + pub fn hash(b: []const u8, out: []u8) { var d = Self.init(); d.update(b); - return d.final(); + d.final(out); } pub fn update(d: &Self, b: []const u8) { @@ -91,22 +92,18 @@ fn Blake2s(comptime out_len: usize) -> type { return struct { d.buf_len += u8(b[off..].len); } - pub fn final(d: &Self) -> ReturnType { + pub fn final(d: &Self, out: []u8) { + debug.assert(out.len >= out_len / 8); + mem.set(u8, d.buf[d.buf_len..], 0); d.t += d.buf_len; d.round(d.buf[0..], true); const rr = d.h[0 .. out_len / 32]; - // NOTE: mem.readIntLE or equivalent would be useful here. - var j: u8 = 0; - var r: ReturnType = 0; - for (rr) |p| { - r |= ReturnType(p) << j; - j +%= 32; + for (rr) |s, j| { + mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Little); } - - return std.endian.swapIfLe(ReturnType, r); } fn round(d: &Self, b: []const u8, last: bool) { @@ -160,66 +157,74 @@ fn Blake2s(comptime out_len: usize) -> type { return struct { } };} -// TODO: bigint rem >1 digits for 224 integer output. -// -// test "blake2s224 single" { -// const hash1 = 0xa847d26c2f966c5c4cc222b174918a56037cdee34b3f872f; -// debug.assert(hash1 == Blake2s224.hash("")); -// -// const hash2 = 0x1e2ed10fcdbc46e0ab3ea3f268a6c288083ae04e3d63a8de; -// debug.assert(hash2 == Blake2s224.hash("abc")); -// -// const hash3 = 0xe486adf7b22d2944b434ae78ae64720c16ccf0479dab072d; -// debug.assert(hash3 == Blake2s224.hash("The quick brown fox jumps over the lazy dog")); -// } -// -// test "blake2s224 streaming" { -// var h = Blake2s224.init(); -// -// const hash1 = 0xa847d26c2f966c5c4cc222b174918a56037cdee34b3f872f; -// debug.assert(hash1 == h.final()); -// -// const hash2 = 0x1e2ed10fcdbc46e0ab3ea3f268a6c288083ae04e3d63a8de; -// -// h.reset(); -// h.update("abc"); -// debug.assert(hash2 == h.final()); -// -// h.reset(); -// h.update("a"); -// h.update("b"); -// h.update("c"); -// debug.assert(hash2 == h.final()); -// } +test "blake2s224 single" { + const h1 = "1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4"; + htest.assertEqualHash(Blake2s224, h1, ""); + + const h2 = "0b033fc226df7abde29f67a05d3dc62cf271ef3dfea4d387407fbd55"; + htest.assertEqualHash(Blake2s224, h2, "abc"); + + const h3 = "e4e5cb6c7cae41982b397bf7b7d2d9d1949823ae78435326e8db4912"; + htest.assertEqualHash(Blake2s224, h3, "The quick brown fox jumps over the lazy dog"); +} + +test "blake2s224 streaming" { + var h = Blake2s224.init(); + var out: [28]u8 = undefined; + + const h1 = "1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4"; + + h.final(out[0..]); + htest.assertEqual(h1, out[0..]); + + const h2 = "0b033fc226df7abde29f67a05d3dc62cf271ef3dfea4d387407fbd55"; + + h.reset(); + h.update("abc"); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); + + h.reset(); + h.update("a"); + h.update("b"); + h.update("c"); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); +} test "blake2s256 single" { - const hash1 = 0x69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9; - debug.assert(hash1 == Blake2s256.hash("")); + const h1 = "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9"; + htest.assertEqualHash(Blake2s256, h1, ""); - const hash2 = 0x508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982; - debug.assert(hash2 == Blake2s256.hash("abc")); + const h2 = "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"; + htest.assertEqualHash(Blake2s256, h2, "abc"); - const hash3 = 0x606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812; - debug.assert(hash3 == Blake2s256.hash("The quick brown fox jumps over the lazy dog")); + const h3 = "606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812"; + htest.assertEqualHash(Blake2s256, h3, "The quick brown fox jumps over the lazy dog"); } test "blake2s256 streaming" { var h = Blake2s256.init(); + var out: [32]u8 = undefined; + + const h1 = "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9"; - const hash1 = 0x69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9; - debug.assert(hash1 == h.final()); + h.final(out[0..]); + htest.assertEqual(h1, out[0..]); - const hash2 = 0x508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982; + const h2 = "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"; h.reset(); h.update("abc"); - debug.assert(hash2 == h.final()); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); h.reset(); h.update("a"); h.update("b"); h.update("c"); - debug.assert(hash2 == h.final()); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); } @@ -231,8 +236,6 @@ pub const Blake2b512 = Blake2b(512); fn Blake2b(comptime out_len: usize) -> type { return struct { const Self = this; - const ReturnType = @IntType(false, out_len); - const u9 = @IntType(false, 9); const iv = [8]u64 { 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, @@ -279,10 +282,10 @@ fn Blake2b(comptime out_len: usize) -> type { return struct { d.buf_len = 0; } - pub fn hash(b: []const u8) -> ReturnType { + pub fn hash(b: []const u8, out: []u8) { var d = Self.init(); d.update(b); - return d.final(); + d.final(out); } pub fn update(d: &Self, b: []const u8) { @@ -308,21 +311,16 @@ fn Blake2b(comptime out_len: usize) -> type { return struct { d.buf_len += u8(b[off..].len); } - pub fn final(d: &Self) -> ReturnType { + pub fn final(d: &Self, out: []u8) { mem.set(u8, d.buf[d.buf_len..], 0); d.t += d.buf_len; d.round(d.buf[0..], true); const rr = d.h[0 .. out_len / 64]; - var j: u9 = 0; - var r: ReturnType = 0; - for (rr) |p| { - r |= ReturnType(p) << j; - j +%= 64; + for (rr) |s, j| { + mem.writeInt(out[8*j .. 8*j + 8], s, builtin.Endian.Little); } - - return std.endian.swapIfLe(ReturnType, r); } fn round(d: &Self, b: []const u8, last: bool) { @@ -376,64 +374,72 @@ fn Blake2b(comptime out_len: usize) -> type { return struct { } };} -// TODO: bigint rem >1 digits for 384 integer output. - -// test "blake2b384 single" { -// const hash1 = 0xb32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100; -// debug.assert(hash1 == Blake2b384.hash("")); -// -// const hash2 = 0x6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4; -// debug.assert(hash2 == Blake2b384.hash("abc")); -// -// const hash3 = 0xb7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d; -// debug.assert(hash3 == Blake2b384.hash("The quick brown fox jumps over the lazy dog")); -// } -// -// test "blake2b384 streaming" { -// var h = Blake2b384.init(); -// -// const hash1 = 0xb32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100; -// debug.assert(hash1 == h.final()); -// -// const hash2 = 0x6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4; -// -// h.reset(); -// h.update("abc"); -// debug.assert(hash2 == h.final()); -// -// h.reset(); -// h.update("a"); -// h.update("b"); -// h.update("c"); -// debug.assert(hash2 == h.final()); -// } +test "blake2b384 single" { + const h1 = "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100"; + htest.assertEqualHash(Blake2b384, h1, ""); + + const h2 = "6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4"; + htest.assertEqualHash(Blake2b384, h2, "abc"); + + const h3 = "b7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d"; + htest.assertEqualHash(Blake2b384, h3, "The quick brown fox jumps over the lazy dog"); +} + +test "blake2b384 streaming" { + var h = Blake2b384.init(); + var out: [48]u8 = undefined; + + const h1 = "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100"; + + h.final(out[0..]); + htest.assertEqual(h1, out[0..]); + + const h2 = "6f56a82c8e7ef526dfe182eb5212f7db9df1317e57815dbda46083fc30f54ee6c66ba83be64b302d7cba6ce15bb556f4"; + + h.reset(); + h.update("abc"); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); + + h.reset(); + h.update("a"); + h.update("b"); + h.update("c"); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); +} test "blake2b512 single" { - const hash1 = 0x786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce; - debug.assert(hash1 == Blake2b512.hash("")); + const h1 = "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"; + htest.assertEqualHash(Blake2b512, h1, ""); - const hash2 = 0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923; - debug.assert(hash2 == Blake2b512.hash("abc")); + const h2 = "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"; + htest.assertEqualHash(Blake2b512, h2, "abc"); - const hash3 = 0xa8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918; - debug.assert(hash3 == Blake2b512.hash("The quick brown fox jumps over the lazy dog")); + const h3 = "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918"; + htest.assertEqualHash(Blake2b512, h3, "The quick brown fox jumps over the lazy dog"); } test "blake2b512 streaming" { var h = Blake2b512.init(); + var out: [64]u8 = undefined; + + const h1 = "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"; - const hash1 = 0x786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce; - debug.assert(hash1 == h.final()); + h.final(out[0..]); + htest.assertEqual(h1, out[0..]); - const hash2 = 0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923; + const h2 = "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"; h.reset(); h.update("abc"); - debug.assert(hash2 == h.final()); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); h.reset(); h.update("a"); h.update("b"); h.update("c"); - debug.assert(hash2 == h.final()); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); } diff --git a/std/crypto/index.zig b/std/crypto/index.zig index a63984e3d5dc..839704e3e703 100644 --- a/std/crypto/index.zig +++ b/std/crypto/index.zig @@ -7,15 +7,15 @@ pub const Sha256 = sha2.Sha256; pub const Sha384 = sha2.Sha384; pub const Sha512 = sha2.Sha512; -const blake2x = @import("blake2x.zig"); -pub const Blake2s224 = blake2x.Blake2s224; -pub const Blake2s256 = blake2x.Blake2s256; -pub const Blake2b384 = blake2x.Blake2b384; -pub const Blake2b512 = blake2x.Blake2b512; +const blake2 = @import("blake2.zig"); +pub const Blake2s224 = blake2.Blake2s224; +pub const Blake2s256 = blake2.Blake2s256; +pub const Blake2b384 = blake2.Blake2b384; +pub const Blake2b512 = blake2.Blake2b512; test "crypto" { _ = @import("md5.zig"); _ = @import("sha1.zig"); _ = @import("sha2.zig"); - _ = @import("blake2x.zig"); + _ = @import("blake2.zig"); } diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig index c01f288c56eb..02f195438083 100644 --- a/std/crypto/md5.zig +++ b/std/crypto/md5.zig @@ -1,7 +1,9 @@ const mem = @import("../mem.zig"); const math = @import("../math/index.zig"); const endian = @import("../endian.zig"); +const builtin = @import("builtin"); const debug = @import("../debug/index.zig"); +const fmt = @import("../fmt/index.zig"); const RoundParam = struct { a: usize, b: usize, c: usize, d: usize, @@ -42,10 +44,10 @@ pub const Md5 = struct { d.total_len = 0; } - pub fn hash(b: []const u8) -> u128 { + pub fn hash(b: []const u8, out: []u8) { var d = Md5.init(); d.update(b); - return d.final(); + d.final(out); } pub fn update(d: &Self, b: []const u8) { @@ -73,7 +75,9 @@ pub const Md5 = struct { d.total_len +%= b.len; } - pub fn final(d: &Self) -> u128 { + pub fn final(d: &Self, out: []u8) { + debug.assert(out.len >= 16); + // The buffer here will never be completely full. mem.set(u8, d.buf[d.buf_len..], 0); @@ -98,13 +102,9 @@ pub const Md5 = struct { d.round(d.buf[0..]); - const r = - (u128(d.s[3]) << 96) | - (u128(d.s[2]) << 64) | - (u128(d.s[1]) << 32) | - (u128(d.s[0]) << 0); - - return endian.swapIfLe(u128, r); + for (d.s) |s, j| { + mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Little); + } } fn round(d: &Self, b: []const u8) { @@ -226,28 +226,35 @@ pub const Md5 = struct { } }; +const htest = @import("test.zig"); + test "md5 single" { - debug.assert(0xd41d8cd98f00b204e9800998ecf8427e == Md5.hash("")); - debug.assert(0x0cc175b9c0f1b6a831c399e269772661 == Md5.hash("a")); - debug.assert(0x900150983cd24fb0d6963f7d28e17f72 == Md5.hash("abc")); - debug.assert(0xf96b697d7cb7938d525a2f31aaf161d0 == Md5.hash("message digest")); - debug.assert(0xc3fcd3d76192e4007dfb496cca67e13b == Md5.hash("abcdefghijklmnopqrstuvwxyz")); - debug.assert(0xd174ab98d277d9f5a5611c2c9f419d9f == Md5.hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")); - debug.assert(0x57edf4a22be3c955ac49da2e2107b67a == Md5.hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890")); + htest.assertEqualHash(Md5, "d41d8cd98f00b204e9800998ecf8427e", ""); + htest.assertEqualHash(Md5, "0cc175b9c0f1b6a831c399e269772661", "a"); + htest.assertEqualHash(Md5, "900150983cd24fb0d6963f7d28e17f72", "abc"); + htest.assertEqualHash(Md5, "f96b697d7cb7938d525a2f31aaf161d0", "message digest"); + htest.assertEqualHash(Md5, "c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz"); + htest.assertEqualHash(Md5, "d174ab98d277d9f5a5611c2c9f419d9f", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + htest.assertEqualHash(Md5, "57edf4a22be3c955ac49da2e2107b67a", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"); } test "md5 streaming" { var h = Md5.init(); + var out: [16]u8 = undefined; - debug.assert(0xd41d8cd98f00b204e9800998ecf8427e == h.final()); + h.final(out[0..]); + htest.assertEqual("d41d8cd98f00b204e9800998ecf8427e", out[0..]); h.reset(); h.update("abc"); - debug.assert(0x900150983cd24fb0d6963f7d28e17f72 == h.final()); + h.final(out[0..]); + htest.assertEqual("900150983cd24fb0d6963f7d28e17f72", out[0..]); h.reset(); h.update("a"); h.update("b"); h.update("c"); - debug.assert(0x900150983cd24fb0d6963f7d28e17f72 == h.final()); + h.final(out[0..]); + + htest.assertEqual("900150983cd24fb0d6963f7d28e17f72", out[0..]); } diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig index 5c8278700c78..bb68b62983f6 100644 --- a/std/crypto/sha1.zig +++ b/std/crypto/sha1.zig @@ -2,6 +2,7 @@ const mem = @import("../mem.zig"); const math = @import("../math/index.zig"); const endian = @import("../endian.zig"); const debug = @import("../debug/index.zig"); +const builtin = @import("builtin"); pub const u160 = @IntType(false, 160); @@ -38,10 +39,10 @@ pub const Sha1 = struct { d.total_len = 0; } - pub fn hash(b: []const u8) -> u160 { + pub fn hash(b: []const u8, out: []u8) { var d = Sha1.init(); d.update(b); - return d.final(); + d.final(out); } pub fn update(d: &Self, b: []const u8) { @@ -68,7 +69,9 @@ pub const Sha1 = struct { d.total_len += b.len; } - pub fn final(d: &Self) -> u160 { + pub fn final(d: &Self, out: []u8) { + debug.assert(out.len >= 20); + // The buffer here will never be completely full. mem.set(u8, d.buf[d.buf_len..], 0); @@ -93,14 +96,9 @@ pub const Sha1 = struct { d.round(d.buf[0..]); - const r = - (u160(d.s[0]) << 128) | - (u160(d.s[1]) << 96) | - (u160(d.s[2]) << 64) | - (u160(d.s[3]) << 32) | - (u160(d.s[4]) << 0); - - return endian.swapIfBe(u160, r); + for (d.s) |s, j| { + mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Big); + } } fn round(d: &Self, b: []const u8) { @@ -257,24 +255,30 @@ pub const Sha1 = struct { } }; +const htest = @import("test.zig"); + test "sha1 single" { - debug.assert(0xda39a3ee5e6b4b0d3255bfef95601890afd80709 == Sha1.hash("")); - debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == Sha1.hash("abc")); - debug.assert(0xa49b2446a02c645bf419f995b67091253a04a259 == Sha1.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")); + htest.assertEqualHash(Sha1, "da39a3ee5e6b4b0d3255bfef95601890afd80709", ""); + htest.assertEqualHash(Sha1, "a9993e364706816aba3e25717850c26c9cd0d89d", "abc"); + htest.assertEqualHash(Sha1, "a49b2446a02c645bf419f995b67091253a04a259", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); } test "sha1 streaming" { var h = Sha1.init(); + var out: [20]u8 = undefined; - debug.assert(0xda39a3ee5e6b4b0d3255bfef95601890afd80709 == h.final()); + h.final(out[0..]); + htest.assertEqual("da39a3ee5e6b4b0d3255bfef95601890afd80709", out[0..]); h.reset(); h.update("abc"); - debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == h.final()); + h.final(out[0..]); + htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]); h.reset(); h.update("a"); h.update("b"); h.update("c"); - debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == h.final()); + h.final(out[0..]); + htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]); } diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig index cfe4640fe788..6712a880a996 100644 --- a/std/crypto/sha2.zig +++ b/std/crypto/sha2.zig @@ -3,6 +3,7 @@ const math = @import("../math/index.zig"); const endian = @import("../endian.zig"); const debug = @import("../debug/index.zig"); const builtin = @import("builtin"); +const htest = @import("test.zig"); ///////////////////// // Sha224 + Sha256 @@ -57,7 +58,6 @@ pub const Sha256 = Sha2_32(Sha256Params); fn Sha2_32(comptime params: Sha2Params32) -> type { return struct { const Self = this; - const ReturnType = @IntType(false, params.out_len); s: [8]u32, // Streaming Cache @@ -84,10 +84,10 @@ fn Sha2_32(comptime params: Sha2Params32) -> type { return struct { d.total_len = 0; } - pub fn hash(b: []const u8) -> ReturnType { + pub fn hash(b: []const u8, out: []u8) { var d = Self.init(); d.update(b); - return d.final(); + d.final(out); } pub fn update(d: &Self, b: []const u8) { @@ -114,7 +114,9 @@ fn Sha2_32(comptime params: Sha2Params32) -> type { return struct { d.total_len += b.len; } - pub fn final(d: &Self) -> ReturnType { + pub fn final(d: &Self, out: []u8) { + debug.assert(out.len >= params.out_len / 8); + // The buffer here will never be completely full. mem.set(u8, d.buf[d.buf_len..], 0); @@ -142,14 +144,9 @@ fn Sha2_32(comptime params: Sha2Params32) -> type { return struct { // May truncate for possible 224 output const rr = d.s[0 .. params.out_len / 32]; - var j: u8 = u8(rr.len - 1) * 32; - var r: ReturnType = 0; - for (rr) |p| { - r |= ReturnType(p) << j; - j -%= 32; + for (rr) |s, j| { + mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Big); } - - return endian.swapIfBe(ReturnType, r); } fn round(d: &Self, b: []const u8) { @@ -275,9 +272,9 @@ test "sha224 single" { return; } - debug.assert(0xd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f == Sha224.hash("")); - debug.assert(0x23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7 == Sha224.hash("abc")); - debug.assert(0xc97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3 == Sha224.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")); + htest.assertEqualHash(Sha224, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""); + htest.assertEqualHash(Sha224, "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc"); + htest.assertEqualHash(Sha224, "c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); } test "sha224 streaming" { @@ -287,18 +284,22 @@ test "sha224 streaming" { } var h = Sha224.init(); + var out: [28]u8 = undefined; - debug.assert(0xd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f == h.final()); + h.final(out[0..]); + htest.assertEqual("d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", out[0..]); h.reset(); h.update("abc"); - debug.assert(0x23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7 == h.final()); + h.final(out[0..]); + htest.assertEqual("23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", out[0..]); h.reset(); h.update("a"); h.update("b"); h.update("c"); - debug.assert(0x23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7 == h.final()); + h.final(out[0..]); + htest.assertEqual("23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", out[0..]); } test "sha256 single" { @@ -307,9 +308,9 @@ test "sha256 single" { return; } - debug.assert(0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == Sha256.hash("")); - debug.assert(0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad == Sha256.hash("abc")); - debug.assert(0xcf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1 == Sha256.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")); + htest.assertEqualHash(Sha256, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""); + htest.assertEqualHash(Sha256, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc"); + htest.assertEqualHash(Sha256, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); } test "sha256 streaming" { @@ -319,18 +320,22 @@ test "sha256 streaming" { } var h = Sha256.init(); + var out: [32]u8 = undefined; - debug.assert(0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == h.final()); + h.final(out[0..]); + htest.assertEqual("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", out[0..]); h.reset(); h.update("abc"); - debug.assert(0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad == h.final()); + h.final(out[0..]); + htest.assertEqual("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", out[0..]); h.reset(); h.update("a"); h.update("b"); h.update("c"); - debug.assert(0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad == h.final()); + h.final(out[0..]); + htest.assertEqual("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", out[0..]); } @@ -387,7 +392,6 @@ pub const Sha512 = Sha2_64(Sha512Params); fn Sha2_64(comptime params: Sha2Params64) -> type { return struct { const Self = this; - const ReturnType = @IntType(false, params.out_len); const u9 = @IntType(false, 9); s: [8]u64, @@ -415,10 +419,10 @@ fn Sha2_64(comptime params: Sha2Params64) -> type { return struct { d.total_len = 0; } - pub fn hash(b: []const u8) -> ReturnType { + pub fn hash(b: []const u8, out: []u8) { var d = Self.init(); d.update(b); - return d.final(); + d.final(out); } pub fn update(d: &Self, b: []const u8) { @@ -445,7 +449,9 @@ fn Sha2_64(comptime params: Sha2Params64) -> type { return struct { d.total_len += b.len; } - pub fn final(d: &Self) -> ReturnType { + pub fn final(d: &Self, out: []u8) { + debug.assert(out.len >= params.out_len / 8); + // The buffer here will never be completely full. mem.set(u8, d.buf[d.buf_len..], 0); @@ -473,14 +479,9 @@ fn Sha2_64(comptime params: Sha2Params64) -> type { return struct { // May truncate for possible 384 output const rr = d.s[0 .. params.out_len / 64]; - var j: u9 = u9(rr.len - 1) * 64; - var r: ReturnType = 0; - for (rr) |p| { - r |= ReturnType(p) << j; - j -%= 64; + for (rr) |s, j| { + mem.writeInt(out[8*j .. 8*j + 8], s, builtin.Endian.Big); } - - return endian.swapIfBe(ReturnType, r); } fn round(d: &Self, b: []const u8) { @@ -626,14 +627,14 @@ test "sha384 single" { return; } - const h1 = 0x38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b; - debug.assert(h1 == Sha384.hash("")); + const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"; + htest.assertEqualHash(Sha384, h1, ""); - const h2 = 0xcb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7; - debug.assert(h2 == Sha384.hash("abc")); + const h2 = "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"; + htest.assertEqualHash(Sha384, h2, "abc"); - const h3 = 0x09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039; - debug.assert(h3 == Sha384.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")); + const h3 = "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039"; + htest.assertEqualHash(Sha384, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); } test "sha384 streaming" { @@ -643,21 +644,25 @@ test "sha384 streaming" { } var h = Sha384.init(); + var out: [48]u8 = undefined; - const h1 = 0x38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b; - debug.assert(h1 == h.final()); + const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"; + h.final(out[0..]); + htest.assertEqual(h1, out[0..]); - const h2 = 0xcb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7; + const h2 = "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"; h.reset(); h.update("abc"); - debug.assert(h2 == h.final()); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); h.reset(); h.update("a"); h.update("b"); h.update("c"); - debug.assert(h2 == h.final()); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); } test "sha512 single" { @@ -666,14 +671,14 @@ test "sha512 single" { return; } - const h1 = 0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e; - debug.assert(h1 == Sha512.hash("")); + const h1 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"; + htest.assertEqualHash(Sha512, h1, ""); - const h2 = 0xddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f; - debug.assert(h2 == Sha512.hash("abc")); + const h2 = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"; + htest.assertEqualHash(Sha512, h2, "abc"); - const h3 = 0x8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909; - debug.assert(h3 == Sha512.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")); + const h3 = "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"; + htest.assertEqualHash(Sha512, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); } test "sha512 streaming" { @@ -683,19 +688,23 @@ test "sha512 streaming" { } var h = Sha512.init(); + var out: [64]u8 = undefined; - const h1 = 0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e; - debug.assert(h1 == h.final()); + const h1 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"; + h.final(out[0..]); + htest.assertEqual(h1, out[0..]); - const h2 = 0xddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f; + const h2 = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"; h.reset(); h.update("abc"); - debug.assert(h2 == h.final()); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); h.reset(); h.update("a"); h.update("b"); h.update("c"); - debug.assert(h2 == h.final()); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); } From 73b4f098457e141854c26f5f43b2858909c831a3 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Wed, 17 Jan 2018 00:20:20 +1300 Subject: [PATCH 3/3] Add crypto internal test functions --- std/crypto/test.zig | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 std/crypto/test.zig diff --git a/std/crypto/test.zig b/std/crypto/test.zig new file mode 100644 index 000000000000..a35798890178 --- /dev/null +++ b/std/crypto/test.zig @@ -0,0 +1,22 @@ +const debug = @import("../debug/index.zig"); +const mem = @import("../mem.zig"); +const fmt = @import("../fmt/index.zig"); + +// Hash using the specified hasher `H` asserting `expected == H(input)`. +pub fn assertEqualHash(comptime Hasher: var, comptime expected: []const u8, input: []const u8) { + var h: [expected.len / 2]u8 = undefined; + Hasher.hash(input, h[0..]); + + assertEqual(expected, h); +} + +// Assert `expected` == `input` where `input` is a bytestring. +pub fn assertEqual(comptime expected: []const u8, input: []const u8) { + var expected_bytes: [expected.len / 2]u8 = undefined; + for (expected_bytes) |*r, i| { + *r = fmt.parseInt(u8, expected[2*i .. 2*i+2], 16) catch unreachable; + } + + debug.assert(mem.eql(u8, expected_bytes, input)); +} +