From 304f6f1d0165f9bdacb7d80479298ca0acff1c27 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sat, 13 Jan 2018 13:23:12 +1300 Subject: [PATCH 1/2] Add integer rotation functions --- std/math/index.zig | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/std/math/index.zig b/std/math/index.zig index 081c59a88aa051f2efe3f3a01db983274a3e3c85..d925740030df1c092f5e60768b0fbe5342f4631b 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -267,6 +267,45 @@ test "math.shr" { assert(shr(u8, 0b11111111, isize(-2)) == 0b11111100); } +/// Rotates right. Only unsigned values can be rotated. +/// Negative shift values results in shift modulo the bit count. +pub fn rotr(comptime T: type, x: T, r: var) -> T { + if (T.is_signed) { + @compileError("cannot rotate signed integer"); + } else { + const ar = @mod(r, T.bit_count); + return shr(T, x, ar) | shl(T, x, T.bit_count - ar); + } +} + +test "math.rotr" { + assert(rotr(u8, 0b00000001, usize(0)) == 0b00000001); + assert(rotr(u8, 0b00000001, usize(9)) == 0b10000000); + assert(rotr(u8, 0b00000001, usize(8)) == 0b00000001); + assert(rotr(u8, 0b00000001, usize(4)) == 0b00010000); + assert(rotr(u8, 0b00000001, isize(-1)) == 0b00000010); +} + +/// Rotates left. Only unsigned values can be rotated. +/// Negative shift values results in shift modulo the bit count. +pub fn rotl(comptime T: type, x: T, r: var) -> T { + if (T.is_signed) { + @compileError("cannot rotate signed integer"); + } else { + const ar = @mod(r, T.bit_count); + return shl(T, x, ar) | shr(T, x, T.bit_count - ar); + } +} + +test "math.rotl" { + assert(rotl(u8, 0b00000001, usize(0)) == 0b00000001); + assert(rotl(u8, 0b00000001, usize(9)) == 0b00000010); + assert(rotl(u8, 0b00000001, usize(8)) == 0b00000001); + assert(rotl(u8, 0b00000001, usize(4)) == 0b00010000); + assert(rotl(u8, 0b00000001, isize(-1)) == 0b10000000); +} + + pub fn Log2Int(comptime T: type) -> type { return @IntType(false, log2(T.bit_count)); } -- 2.54.0 From 51fdbf7f8c282c7b91a688c9069b363e90bedf6e Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sat, 13 Jan 2018 14:40:21 +1300 Subject: [PATCH 2/2] Add Md5 and Sha1 hash functions Some performance comparisons to C. We take the fastest time measurement taken across multiple runs. The block hashing functions use the same md5/sha1 methods. ``` Cpu: Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz Gcc: 7.2.1 20171224 Clang: 5.0.1 Zig: 0.1.1.304f6f1d ``` See https://www.nayuki.io/page/fast-md5-hash-implementation-in-x86-assembly: ``` gcc -O2 661 Mb/s clang -O2 490 Mb/s zig --release-fast and zig --release-safe 570 Mb/s zig 50 Mb/s ``` See https://www.nayuki.io/page/fast-sha1-hash-implementation-in-x86-assembly: ``` gcc -O2 588 Mb/s clang -O2 563 Mb/s zig --release-fast and zig --release-safe 610 Mb/s zig 21 Mb/s ``` In short, zig provides pretty useful tools for writing this sort of code. We are in the lead against clang (which uses the same LLVM backend) with us being slower only against md5 with GCC. --- CMakeLists.txt | 3 + std/crypto/index.zig | 7 ++ std/crypto/md5.zig | 252 ++++++++++++++++++++++++++++++++++++++ std/crypto/sha1.zig | 280 +++++++++++++++++++++++++++++++++++++++++++ std/index.zig | 2 + 5 files changed, 544 insertions(+) create mode 100644 std/crypto/index.zig create mode 100644 std/crypto/md5.zig create mode 100644 std/crypto/sha1.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 252b07e6ddae0a766bd1ad35d4983f35d5a662e7..1e9691f1c620beef18f84d4fe58e42f278dc8f22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -364,6 +364,9 @@ set(ZIG_STD_FILES "c/index.zig" "c/linux.zig" "c/windows.zig" + "crypto/index.zig" + "crypto/md5.zig" + "crypto/sha1.zig" "cstr.zig" "debug/failing_allocator.zig" "debug/index.zig" diff --git a/std/crypto/index.zig b/std/crypto/index.zig new file mode 100644 index 0000000000000000000000000000000000000000..eaa371555a626ffa188aa1eabac66165ae248683 --- /dev/null +++ b/std/crypto/index.zig @@ -0,0 +1,7 @@ +pub const Sha1 = @import("md5.zig").Sha1; +pub const Md5 = @import("sha1.zig").Md5; + +test "crypto" { + _ = @import("md5.zig"); + _ = @import("sha1.zig"); +} diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig new file mode 100644 index 0000000000000000000000000000000000000000..a324e4b8c6ff2d4cf8a8c2b0d2f6485506fd6111 --- /dev/null +++ b/std/crypto/md5.zig @@ -0,0 +1,252 @@ +const mem = @import("../mem.zig"); +const math = @import("../math/index.zig"); +const endian = @import("../endian.zig"); +const debug = @import("../debug/index.zig"); + +const RoundParam = struct { + a: u32, b: u32, c: u32, d: u32, + k: u32, s: u32, t: u32 +}; + +fn Rp(a: u32, b: u32, c: u32, d: u32, k: u32, s: u5, t: u32) -> RoundParam { + return RoundParam { .a = a, .b = b, .c = c, .d = d, .k = k, .s = s, .t = t }; +} + +/// const hash1 = Md5.hash("my input"); +/// +/// const hasher = Md5.init(); +/// hasher.update("my "); +/// hasher.update("input"); +/// const hash2 = hasher.final(); +pub const Md5 = struct { + const Self = this; + + s: [4]u32, + // Streaming Cache + buf: [64]u8, + buf_len: u8, + total_len: u64, + + pub fn init() -> Self { + var d: Self = undefined; + d.reset(); + return d; + } + + pub fn reset(d: &Self) { + d.s[0] = 0x67452301; + d.s[1] = 0xEFCDAB89; + d.s[2] = 0x98BADCFE; + d.s[3] = 0x10325476; + d.buf_len = 0; + d.total_len = 0; + } + + pub fn hash(b: []const u8) -> u128 { + var d = Md5.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.round(d.buf[0..]); + d.buf_len = 0; + } + + // Full middle blocks. + while (off + 64 < b.len) : (off += 64) { + d.round(b[off..off + 64]); + } + + // Copy any remainder for next pass. + mem.copy(u8, d.buf[d.buf_len..], b[off..]); + d.buf_len += u8(b[off..].len); + + d.total_len += b.len; + } + + pub fn final(d: &Self) -> u128 { + // The buffer here will never be completely full. + mem.set(u8, d.buf[d.buf_len..], 0); + + // Append padding bits. + d.buf[d.buf_len] = 0x80; + d.buf_len += 1; + + // > 448 mod 512 so need to add an extra round to wrap around. + if (64 - d.buf_len < 8) { + d.round(d.buf[0..]); + mem.set(u8, d.buf[0..], 0); + } + + // Append message length. + var i: usize = 1; + var len = d.total_len >> 5; + d.buf[56] = u8(d.total_len & 0x1f) << 3; + while (i < 8) : (i += 1) { + d.buf[56 + i] = u8(len & 0xff); + len >>= 8; + } + + 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); + } + + fn round(d: &Self, b: []const u8) { + debug.assert(b.len == 64); + + var s: [16]u32 = undefined; + + // ERROR: cannot unroll this at comptime + var i: usize = 0; + while (i < 16) : (i += 1) { + // NOTE: Performing or's separately improves perf by ~10% + s[i] = 0; + s[i] |= u32(b[i*4+0]); + s[i] |= u32(b[i*4+1]) << 8; + s[i] |= u32(b[i*4+2]) << 16; + s[i] |= u32(b[i*4+3]) << 24; + } + + var v: [4]u32 = []u32 { + d.s[0], d.s[1], d.s[2], d.s[3], + }; + + const round0 = comptime []RoundParam { + Rp(0, 1, 2, 3, 0, 7, 0xD76AA478), + Rp(3, 0, 1, 2, 1, 12, 0xE8C7B756), + Rp(2, 3, 0, 1, 2, 17, 0x242070DB), + Rp(1, 2, 3, 0, 3, 22, 0xC1BDCEEE), + Rp(0, 1, 2, 3, 4, 7, 0xF57C0FAF), + Rp(3, 0, 1, 2, 5, 12, 0x4787C62A), + Rp(2, 3, 0, 1, 6, 17, 0xA8304613), + Rp(1, 2, 3, 0, 7, 22, 0xFD469501), + Rp(0, 1, 2, 3, 8, 7, 0x698098D8), + Rp(3, 0, 1, 2, 9, 12, 0x8B44F7AF), + Rp(2, 3, 0, 1, 10, 17, 0xFFFF5BB1), + Rp(1, 2, 3, 0, 11, 22, 0x895CD7BE), + Rp(0, 1, 2, 3, 12, 7, 0x6B901122), + Rp(3, 0, 1, 2, 13, 12, 0xFD987193), + Rp(2, 3, 0, 1, 14, 17, 0xA679438E), + Rp(1, 2, 3, 0, 15, 22, 0x49B40821), + }; + inline for (round0) |r| { + v[r.a] = v[r.a] +% (v[r.d] ^ (v[r.b] & (v[r.c] ^ v[r.d]))) +% r.t +% s[r.k]; + v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); + } + + const round1 = comptime []RoundParam { + Rp(0, 1, 2, 3, 1, 5, 0xF61E2562), + Rp(3, 0, 1, 2, 6, 9, 0xC040B340), + Rp(2, 3, 0, 1, 11, 14, 0x265E5A51), + Rp(1, 2, 3, 0, 0, 20, 0xE9B6C7AA), + Rp(0, 1, 2, 3, 5, 5, 0xD62F105D), + Rp(3, 0, 1, 2, 10, 9, 0x02441453), + Rp(2, 3, 0, 1, 15, 14, 0xD8A1E681), + Rp(1, 2, 3, 0, 4, 20, 0xE7D3FBC8), + Rp(0, 1, 2, 3, 9, 5, 0x21E1CDE6), + Rp(3, 0, 1, 2, 14, 9, 0xC33707D6), + Rp(2, 3, 0, 1, 3, 14, 0xF4D50D87), + Rp(1, 2, 3, 0, 8, 20, 0x455A14ED), + Rp(0, 1, 2, 3, 13, 5, 0xA9E3E905), + Rp(3, 0, 1, 2, 2, 9, 0xFCEFA3F8), + Rp(2, 3, 0, 1, 7, 14, 0x676F02D9), + Rp(1, 2, 3, 0, 12, 20, 0x8D2A4C8A), + }; + inline for (round1) |r| { + v[r.a] = v[r.a] +% (v[r.c] ^ (v[r.d] & (v[r.b] ^ v[r.c]))) +% r.t +% s[r.k]; + v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); + } + + const round2 = comptime []RoundParam { + Rp(0, 1, 2, 3, 5, 4, 0xFFFA3942), + Rp(3, 0, 1, 2, 8, 11, 0x8771F681), + Rp(2, 3, 0, 1, 11, 16, 0x6D9D6122), + Rp(1, 2, 3, 0, 14, 23, 0xFDE5380C), + Rp(0, 1, 2, 3, 1, 4, 0xA4BEEA44), + Rp(3, 0, 1, 2, 4, 11, 0x4BDECFA9), + Rp(2, 3, 0, 1, 7, 16, 0xF6BB4B60), + Rp(1, 2, 3, 0, 10, 23, 0xBEBFBC70), + Rp(0, 1, 2, 3, 13, 4, 0x289B7EC6), + Rp(3, 0, 1, 2, 0, 11, 0xEAA127FA), + Rp(2, 3, 0, 1, 3, 16, 0xD4EF3085), + Rp(1, 2, 3, 0, 6, 23, 0x04881D05), + Rp(0, 1, 2, 3, 9, 4, 0xD9D4D039), + Rp(3, 0, 1, 2, 12, 11, 0xE6DB99E5), + Rp(2, 3, 0, 1, 15, 16, 0x1FA27CF8), + Rp(1, 2, 3, 0, 2, 23, 0xC4AC5665), + }; + inline for (round2) |r| { + v[r.a] = v[r.a] +% (v[r.b] ^ v[r.c] ^ v[r.d]) +% r.t +% s[r.k]; + v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); + } + + const round3 = comptime []RoundParam { + Rp(0, 1, 2, 3, 0, 6, 0xF4292244), + Rp(3, 0, 1, 2, 7, 10, 0x432AFF97), + Rp(2, 3, 0, 1, 14, 15, 0xAB9423A7), + Rp(1, 2, 3, 0, 5, 21, 0xFC93A039), + Rp(0, 1, 2, 3, 12, 6, 0x655B59C3), + Rp(3, 0, 1, 2, 3, 10, 0x8F0CCC92), + Rp(2, 3, 0, 1, 10, 15, 0xFFEFF47D), + Rp(1, 2, 3, 0, 1, 21, 0x85845DD1), + Rp(0, 1, 2, 3, 8, 6, 0x6FA87E4F), + Rp(3, 0, 1, 2, 15, 10, 0xFE2CE6E0), + Rp(2, 3, 0, 1, 6, 15, 0xA3014314), + Rp(1, 2, 3, 0, 13, 21, 0x4E0811A1), + Rp(0, 1, 2, 3, 4, 6, 0xF7537E82), + Rp(3, 0, 1, 2, 11, 10, 0xBD3AF235), + Rp(2, 3, 0, 1, 2, 15, 0x2AD7D2BB), + Rp(1, 2, 3, 0, 9, 21, 0xEB86D391), + }; + inline for (round3) |r| { + v[r.a] = v[r.a] +% (v[r.c] ^ (v[r.b] | ~v[r.d])) +% r.t +% s[r.k]; + v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s); + } + + d.s[0] +%= v[0]; + d.s[1] +%= v[1]; + d.s[2] +%= v[2]; + d.s[3] +%= v[3]; + } +}; + +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")); +} + +test "md5 streaming" { + var h = Md5.init(); + + debug.assert(0xd41d8cd98f00b204e9800998ecf8427e == h.final()); + + h.reset(); + h.update("abc"); + debug.assert(0x900150983cd24fb0d6963f7d28e17f72 == h.final()); + + h.reset(); + h.update("a"); + h.update("b"); + h.update("c"); + debug.assert(0x900150983cd24fb0d6963f7d28e17f72 == h.final()); +} diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig new file mode 100644 index 0000000000000000000000000000000000000000..6a6d8ba3eeb7528b630fe6aed5536d697dc565e3 --- /dev/null +++ b/std/crypto/sha1.zig @@ -0,0 +1,280 @@ +const mem = @import("../mem.zig"); +const math = @import("../math/index.zig"); +const endian = @import("../endian.zig"); +const debug = @import("../debug/index.zig"); + +pub const u160 = @IntType(false, 160); + +const RoundParam = struct { + a: u32, b: u32, c: u32, d: u32, e: u32, i: u32, +}; + +fn Rp(a: u32, b: u32, c: u32, d: u32, e: u32, i: u32) -> RoundParam { + return RoundParam { .a = a, .b = b, .c = c, .d = d, .e = e, .i = i }; +} + +pub const Sha1 = struct { + const Self = this; + + s: [5]u32, + // Streaming Cache + buf: [64]u8, + buf_len: u8, + total_len: u64, + + pub fn init() -> Self { + var d: Self = undefined; + d.reset(); + return d; + } + + pub fn reset(d: &Self) { + d.s[0] = 0x67452301; + d.s[1] = 0xEFCDAB89; + d.s[2] = 0x98BADCFE; + d.s[3] = 0x10325476; + d.s[4] = 0xC3D2E1F0; + d.buf_len = 0; + d.total_len = 0; + } + + pub fn hash(b: []const u8) -> u160 { + var d = Sha1.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.round(d.buf[0..]); + d.buf_len = 0; + } + + // Full middle blocks. + while (off + 64 < b.len) : (off += 64) { + d.round(b[off..off + 64]); + } + + // Copy any remainder for next pass. + mem.copy(u8, d.buf[d.buf_len..], b[off..]); + d.buf_len += u8(b[off..].len); + + d.total_len += b.len; + } + + pub fn final(d: &Self) -> u160 { + // The buffer here will never be completely full. + mem.set(u8, d.buf[d.buf_len..], 0); + + // Append padding bits. + d.buf[d.buf_len] = 0x80; + d.buf_len += 1; + + // > 448 mod 512 so need to add an extra round to wrap around. + if (64 - d.buf_len < 8) { + d.round(d.buf[0..]); + mem.set(u8, d.buf[0..], 0); + } + + // Append message length. + var i: usize = 1; + var len = d.total_len >> 5; + d.buf[63] = u8(d.total_len & 0x1f) << 3; + while (i < 8) : (i += 1) { + d.buf[63 - i] = u8(len & 0xff); + len >>= 8; + } + + 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); + } + + fn round(d: &Self, b: []const u8) { + debug.assert(b.len == 64); + + var s: [16]u32 = undefined; + + var v: [5]u32 = []u32 { + d.s[0], d.s[1], d.s[2], d.s[3], d.s[4], + }; + + const round0a = comptime []RoundParam { + Rp(0, 1, 2, 3, 4, 0), + Rp(4, 0, 1, 2, 3, 1), + Rp(3, 4, 0, 1, 2, 2), + Rp(2, 3, 4, 0, 1, 3), + Rp(1, 2, 3, 4, 0, 4), + Rp(0, 1, 2, 3, 4, 5), + Rp(4, 0, 1, 2, 3, 6), + Rp(3, 4, 0, 1, 2, 7), + Rp(2, 3, 4, 0, 1, 8), + Rp(1, 2, 3, 4, 0, 9), + Rp(0, 1, 2, 3, 4, 10), + Rp(4, 0, 1, 2, 3, 11), + Rp(3, 4, 0, 1, 2, 12), + Rp(2, 3, 4, 0, 1, 13), + Rp(1, 2, 3, 4, 0, 14), + Rp(0, 1, 2, 3, 4, 15), + }; + inline for (round0a) |r| { + s[r.i] = (u32(b[r.i * 4 + 0]) << 24) | + (u32(b[r.i * 4 + 1]) << 16) | + (u32(b[r.i * 4 + 2]) << 8) | + (u32(b[r.i * 4 + 3]) << 0); + + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf] + +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d])); + v[r.b] = math.rotl(u32, v[r.b], u32(30)); + } + + const round0b = comptime []RoundParam { + Rp(4, 0, 1, 2, 3, 16), + Rp(3, 4, 0, 1, 2, 17), + Rp(2, 3, 4, 0, 1, 18), + Rp(1, 2, 3, 4, 0, 19), + }; + inline for (round0b) |r| { + const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; + s[r.i & 0xf] = math.rotl(u32, t, u32(1)); + + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf] + +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d])); + v[r.b] = math.rotl(u32, v[r.b], u32(30)); + } + + const round1 = comptime []RoundParam { + Rp(0, 1, 2, 3, 4, 20), + Rp(4, 0, 1, 2, 3, 21), + Rp(3, 4, 0, 1, 2, 22), + Rp(2, 3, 4, 0, 1, 23), + Rp(1, 2, 3, 4, 0, 24), + Rp(0, 1, 2, 3, 4, 25), + Rp(4, 0, 1, 2, 3, 26), + Rp(3, 4, 0, 1, 2, 27), + Rp(2, 3, 4, 0, 1, 28), + Rp(1, 2, 3, 4, 0, 29), + Rp(0, 1, 2, 3, 4, 30), + Rp(4, 0, 1, 2, 3, 31), + Rp(3, 4, 0, 1, 2, 32), + Rp(2, 3, 4, 0, 1, 33), + Rp(1, 2, 3, 4, 0, 34), + Rp(0, 1, 2, 3, 4, 35), + Rp(4, 0, 1, 2, 3, 36), + Rp(3, 4, 0, 1, 2, 37), + Rp(2, 3, 4, 0, 1, 38), + Rp(1, 2, 3, 4, 0, 39), + }; + inline for (round1) |r| { + const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; + s[r.i & 0xf] = math.rotl(u32, t, u32(1)); + + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x6ED9EBA1 +% s[r.i & 0xf] + +% (v[r.b] ^ v[r.c] ^ v[r.d]); + v[r.b] = math.rotl(u32, v[r.b], u32(30)); + } + + const round2 = comptime []RoundParam { + Rp(0, 1, 2, 3, 4, 40), + Rp(4, 0, 1, 2, 3, 41), + Rp(3, 4, 0, 1, 2, 42), + Rp(2, 3, 4, 0, 1, 43), + Rp(1, 2, 3, 4, 0, 44), + Rp(0, 1, 2, 3, 4, 45), + Rp(4, 0, 1, 2, 3, 46), + Rp(3, 4, 0, 1, 2, 47), + Rp(2, 3, 4, 0, 1, 48), + Rp(1, 2, 3, 4, 0, 49), + Rp(0, 1, 2, 3, 4, 50), + Rp(4, 0, 1, 2, 3, 51), + Rp(3, 4, 0, 1, 2, 52), + Rp(2, 3, 4, 0, 1, 53), + Rp(1, 2, 3, 4, 0, 54), + Rp(0, 1, 2, 3, 4, 55), + Rp(4, 0, 1, 2, 3, 56), + Rp(3, 4, 0, 1, 2, 57), + Rp(2, 3, 4, 0, 1, 58), + Rp(1, 2, 3, 4, 0, 59), + }; + inline for (round2) |r| { + const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; + s[r.i & 0xf] = math.rotl(u32, t, u32(1)); + + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x8F1BBCDC +% s[r.i & 0xf] + +% ((v[r.b] & v[r.c]) ^ (v[r.b] & v[r.d]) ^ (v[r.c] & v[r.d])); + v[r.b] = math.rotl(u32, v[r.b], u32(30)); + } + + const round3 = comptime []RoundParam { + Rp(0, 1, 2, 3, 4, 60), + Rp(4, 0, 1, 2, 3, 61), + Rp(3, 4, 0, 1, 2, 62), + Rp(2, 3, 4, 0, 1, 63), + Rp(1, 2, 3, 4, 0, 64), + Rp(0, 1, 2, 3, 4, 65), + Rp(4, 0, 1, 2, 3, 66), + Rp(3, 4, 0, 1, 2, 67), + Rp(2, 3, 4, 0, 1, 68), + Rp(1, 2, 3, 4, 0, 69), + Rp(0, 1, 2, 3, 4, 70), + Rp(4, 0, 1, 2, 3, 71), + Rp(3, 4, 0, 1, 2, 72), + Rp(2, 3, 4, 0, 1, 73), + Rp(1, 2, 3, 4, 0, 74), + Rp(0, 1, 2, 3, 4, 75), + Rp(4, 0, 1, 2, 3, 76), + Rp(3, 4, 0, 1, 2, 77), + Rp(2, 3, 4, 0, 1, 78), + Rp(1, 2, 3, 4, 0, 79), + }; + inline for (round3) |r| { + const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf]; + s[r.i & 0xf] = math.rotl(u32, t, u32(1)); + + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0xCA62C1D6 +% s[r.i & 0xf] + +% (v[r.b] ^ v[r.c] ^ v[r.d]); + v[r.b] = math.rotl(u32, v[r.b], u32(30)); + } + + d.s[0] +%= v[0]; + d.s[1] +%= v[1]; + d.s[2] +%= v[2]; + d.s[3] +%= v[3]; + d.s[4] +%= v[4]; + } +}; + +test "sha1 single" { + debug.assert(0xda39a3ee5e6b4b0d3255bfef95601890afd80709 == Sha1.hash("")); + debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == Sha1.hash("abc")); + debug.assert(0xa49b2446a02c645bf419f995b67091253a04a259 == Sha1.hash("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")); +} + +test "sha1 streaming" { + var h = Sha1.init(); + + debug.assert(0xda39a3ee5e6b4b0d3255bfef95601890afd80709 == h.final()); + + h.reset(); + h.update("abc"); + debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == h.final()); + + h.reset(); + h.update("a"); + h.update("b"); + h.update("c"); + debug.assert(0xa9993e364706816aba3e25717850c26c9cd0d89d == h.final()); +} diff --git a/std/index.zig b/std/index.zig index a9a0038e60b6a68ac3b0ddd375ad87690f4c3cab..3986063068a23bf57e6256048643126f688f2796 100644 --- a/std/index.zig +++ b/std/index.zig @@ -10,6 +10,7 @@ pub const LinkedList = @import("linked_list.zig").LinkedList; pub const base64 = @import("base64.zig"); pub const build = @import("build.zig"); pub const c = @import("c/index.zig"); +pub const crypto = @import("crypto/index.zig"); pub const cstr = @import("cstr.zig"); pub const debug = @import("debug/index.zig"); pub const dwarf = @import("dwarf.zig"); @@ -39,6 +40,7 @@ test "std" { _ = @import("base64.zig"); _ = @import("build.zig"); _ = @import("c/index.zig"); + _ = @import("crypto/index.zig"); _ = @import("cstr.zig"); _ = @import("debug/index.zig"); _ = @import("dwarf.zig"); -- 2.54.0