From 96a4e9b866ee899fcae164693a47b68d18f56257 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Jul 2025 17:09:21 -0700 Subject: [PATCH] std.crypto: fix Sha1 namespace --- lib/std/crypto.zig | 6 +- lib/std/crypto/Sha1.zig | 306 ++++++++++++++++++++++++++++++++++++++ lib/std/crypto/sha1.zig | 319 ---------------------------------------- 3 files changed, 308 insertions(+), 323 deletions(-) create mode 100644 lib/std/crypto/Sha1.zig delete mode 100644 lib/std/crypto/sha1.zig diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index 9307016038..2a45c188b6 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -1,6 +1,6 @@ //! Cryptography. -const root = @import("root"); +const std = @import("std.zig"); pub const timing_safe = @import("crypto/timing_safe.zig"); @@ -118,7 +118,7 @@ pub const hash = struct { pub const blake2 = @import("crypto/blake2.zig"); pub const Blake3 = @import("crypto/blake3.zig").Blake3; pub const Md5 = @import("crypto/md5.zig").Md5; - pub const Sha1 = @import("crypto/sha1.zig").Sha1; + pub const Sha1 = @import("crypto/Sha1.zig"); pub const sha2 = @import("crypto/sha2.zig"); pub const sha3 = @import("crypto/sha3.zig"); pub const composition = @import("crypto/hash_composition.zig"); @@ -216,8 +216,6 @@ pub const random = @import("crypto/tlcsprng.zig").interface; /// Encoding and decoding pub const codecs = @import("crypto/codecs.zig"); -const std = @import("std.zig"); - pub const errors = @import("crypto/errors.zig"); pub const tls = @import("crypto/tls.zig"); diff --git a/lib/std/crypto/Sha1.zig b/lib/std/crypto/Sha1.zig new file mode 100644 index 0000000000..0f57c40482 --- /dev/null +++ b/lib/std/crypto/Sha1.zig @@ -0,0 +1,306 @@ +//! The SHA-1 function is now considered cryptographically broken. +//! Namely, it is feasible to find multiple inputs producing the same hash. +//! For a fast-performing, cryptographically secure hash function, see SHA512/256, BLAKE2 or BLAKE3. + +const std = @import("../std.zig"); +const mem = std.mem; +const math = std.math; +const Sha1 = @This(); + +pub const block_length = 64; +pub const digest_length = 20; +pub const Options = struct {}; + +s: [5]u32, +/// Streaming Cache +buf: [64]u8 = undefined, +buf_len: u8 = 0, +total_len: u64 = 0, + +pub fn init(options: Options) Sha1 { + _ = options; + return .{ + .s = [_]u32{ + 0x67452301, + 0xEFCDAB89, + 0x98BADCFE, + 0x10325476, + 0xC3D2E1F0, + }, + }; +} + +pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void { + var d = Sha1.init(options); + d.update(b); + d.final(out); +} + +pub fn update(d: *Sha1, b: []const u8) void { + 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; + @memcpy(d.buf[d.buf_len..][0..off], 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..][0..64]); + } + + // Copy any remainder for next pass. + @memcpy(d.buf[d.buf_len..][0 .. b.len - off], b[off..]); + d.buf_len += @as(u8, @intCast(b[off..].len)); + + d.total_len += b.len; +} + +pub fn peek(d: Sha1) [digest_length]u8 { + var copy = d; + return copy.finalResult(); +} + +pub fn final(d: *Sha1, out: *[digest_length]u8) void { + // The buffer here will never be completely full. + @memset(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..]); + @memset(d.buf[0..], 0); + } + + // Append message length. + var i: usize = 1; + var len = d.total_len >> 5; + d.buf[63] = @as(u8, @intCast(d.total_len & 0x1f)) << 3; + while (i < 8) : (i += 1) { + d.buf[63 - i] = @as(u8, @intCast(len & 0xff)); + len >>= 8; + } + + d.round(d.buf[0..]); + + for (d.s, 0..) |s, j| { + mem.writeInt(u32, out[4 * j ..][0..4], s, .big); + } +} + +pub fn finalResult(d: *Sha1) [digest_length]u8 { + var result: [digest_length]u8 = undefined; + d.final(&result); + return result; +} + +fn round(d: *Sha1, b: *const [64]u8) void { + 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{ + .abcdei(0, 1, 2, 3, 4, 0), + .abcdei(4, 0, 1, 2, 3, 1), + .abcdei(3, 4, 0, 1, 2, 2), + .abcdei(2, 3, 4, 0, 1, 3), + .abcdei(1, 2, 3, 4, 0, 4), + .abcdei(0, 1, 2, 3, 4, 5), + .abcdei(4, 0, 1, 2, 3, 6), + .abcdei(3, 4, 0, 1, 2, 7), + .abcdei(2, 3, 4, 0, 1, 8), + .abcdei(1, 2, 3, 4, 0, 9), + .abcdei(0, 1, 2, 3, 4, 10), + .abcdei(4, 0, 1, 2, 3, 11), + .abcdei(3, 4, 0, 1, 2, 12), + .abcdei(2, 3, 4, 0, 1, 13), + .abcdei(1, 2, 3, 4, 0, 14), + .abcdei(0, 1, 2, 3, 4, 15), + }; + inline for (round0a) |r| { + s[r.i] = mem.readInt(u32, b[r.i * 4 ..][0..4], .big); + + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(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], @as(u32, 30)); + } + + const round0b = comptime [_]RoundParam{ + .abcdei(4, 0, 1, 2, 3, 16), + .abcdei(3, 4, 0, 1, 2, 17), + .abcdei(2, 3, 4, 0, 1, 18), + .abcdei(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, @as(u32, 1)); + + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(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], @as(u32, 30)); + } + + const round1 = comptime [_]RoundParam{ + .abcdei(0, 1, 2, 3, 4, 20), + .abcdei(4, 0, 1, 2, 3, 21), + .abcdei(3, 4, 0, 1, 2, 22), + .abcdei(2, 3, 4, 0, 1, 23), + .abcdei(1, 2, 3, 4, 0, 24), + .abcdei(0, 1, 2, 3, 4, 25), + .abcdei(4, 0, 1, 2, 3, 26), + .abcdei(3, 4, 0, 1, 2, 27), + .abcdei(2, 3, 4, 0, 1, 28), + .abcdei(1, 2, 3, 4, 0, 29), + .abcdei(0, 1, 2, 3, 4, 30), + .abcdei(4, 0, 1, 2, 3, 31), + .abcdei(3, 4, 0, 1, 2, 32), + .abcdei(2, 3, 4, 0, 1, 33), + .abcdei(1, 2, 3, 4, 0, 34), + .abcdei(0, 1, 2, 3, 4, 35), + .abcdei(4, 0, 1, 2, 3, 36), + .abcdei(3, 4, 0, 1, 2, 37), + .abcdei(2, 3, 4, 0, 1, 38), + .abcdei(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, @as(u32, 1)); + + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(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], @as(u32, 30)); + } + + const round2 = comptime [_]RoundParam{ + .abcdei(0, 1, 2, 3, 4, 40), + .abcdei(4, 0, 1, 2, 3, 41), + .abcdei(3, 4, 0, 1, 2, 42), + .abcdei(2, 3, 4, 0, 1, 43), + .abcdei(1, 2, 3, 4, 0, 44), + .abcdei(0, 1, 2, 3, 4, 45), + .abcdei(4, 0, 1, 2, 3, 46), + .abcdei(3, 4, 0, 1, 2, 47), + .abcdei(2, 3, 4, 0, 1, 48), + .abcdei(1, 2, 3, 4, 0, 49), + .abcdei(0, 1, 2, 3, 4, 50), + .abcdei(4, 0, 1, 2, 3, 51), + .abcdei(3, 4, 0, 1, 2, 52), + .abcdei(2, 3, 4, 0, 1, 53), + .abcdei(1, 2, 3, 4, 0, 54), + .abcdei(0, 1, 2, 3, 4, 55), + .abcdei(4, 0, 1, 2, 3, 56), + .abcdei(3, 4, 0, 1, 2, 57), + .abcdei(2, 3, 4, 0, 1, 58), + .abcdei(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, @as(u32, 1)); + + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(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], @as(u32, 30)); + } + + const round3 = comptime [_]RoundParam{ + .abcdei(0, 1, 2, 3, 4, 60), + .abcdei(4, 0, 1, 2, 3, 61), + .abcdei(3, 4, 0, 1, 2, 62), + .abcdei(2, 3, 4, 0, 1, 63), + .abcdei(1, 2, 3, 4, 0, 64), + .abcdei(0, 1, 2, 3, 4, 65), + .abcdei(4, 0, 1, 2, 3, 66), + .abcdei(3, 4, 0, 1, 2, 67), + .abcdei(2, 3, 4, 0, 1, 68), + .abcdei(1, 2, 3, 4, 0, 69), + .abcdei(0, 1, 2, 3, 4, 70), + .abcdei(4, 0, 1, 2, 3, 71), + .abcdei(3, 4, 0, 1, 2, 72), + .abcdei(2, 3, 4, 0, 1, 73), + .abcdei(1, 2, 3, 4, 0, 74), + .abcdei(0, 1, 2, 3, 4, 75), + .abcdei(4, 0, 1, 2, 3, 76), + .abcdei(3, 4, 0, 1, 2, 77), + .abcdei(2, 3, 4, 0, 1, 78), + .abcdei(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, @as(u32, 1)); + + v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(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], @as(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]; +} + +const RoundParam = struct { + a: usize, + b: usize, + c: usize, + d: usize, + e: usize, + i: u32, + + fn abcdei(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam { + return .{ + .a = a, + .b = b, + .c = c, + .d = d, + .e = e, + .i = i, + }; + } +}; + +const htest = @import("test.zig"); + +test "sha1 single" { + try htest.assertEqualHash(Sha1, "da39a3ee5e6b4b0d3255bfef95601890afd80709", ""); + try htest.assertEqualHash(Sha1, "a9993e364706816aba3e25717850c26c9cd0d89d", "abc"); + try htest.assertEqualHash(Sha1, "a49b2446a02c645bf419f995b67091253a04a259", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); +} + +test "sha1 streaming" { + var h = Sha1.init(.{}); + var out: [20]u8 = undefined; + + h.final(&out); + try htest.assertEqual("da39a3ee5e6b4b0d3255bfef95601890afd80709", out[0..]); + + h = Sha1.init(.{}); + h.update("abc"); + h.final(&out); + try htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]); + + h = Sha1.init(.{}); + h.update("a"); + h.update("b"); + h.update("c"); + h.final(&out); + try htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]); +} + +test "sha1 aligned final" { + var block = [_]u8{0} ** Sha1.block_length; + var out: [Sha1.digest_length]u8 = undefined; + + var h = Sha1.init(.{}); + h.update(&block); + h.final(out[0..]); +} diff --git a/lib/std/crypto/sha1.zig b/lib/std/crypto/sha1.zig deleted file mode 100644 index 55b5312bd3..0000000000 --- a/lib/std/crypto/sha1.zig +++ /dev/null @@ -1,319 +0,0 @@ -const std = @import("../std.zig"); -const mem = std.mem; -const math = std.math; - -const RoundParam = struct { - a: usize, - b: usize, - c: usize, - d: usize, - e: usize, - i: u32, -}; - -fn roundParam(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam { - return RoundParam{ - .a = a, - .b = b, - .c = c, - .d = d, - .e = e, - .i = i, - }; -} - -/// The SHA-1 function is now considered cryptographically broken. -/// Namely, it is feasible to find multiple inputs producing the same hash. -/// For a fast-performing, cryptographically secure hash function, see SHA512/256, BLAKE2 or BLAKE3. -pub const Sha1 = struct { - const Self = @This(); - pub const block_length = 64; - pub const digest_length = 20; - pub const Options = struct {}; - - s: [5]u32, - // Streaming Cache - buf: [64]u8 = undefined, - buf_len: u8 = 0, - total_len: u64 = 0, - - pub fn init(options: Options) Self { - _ = options; - return Self{ - .s = [_]u32{ - 0x67452301, - 0xEFCDAB89, - 0x98BADCFE, - 0x10325476, - 0xC3D2E1F0, - }, - }; - } - - pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void { - var d = Sha1.init(options); - d.update(b); - d.final(out); - } - - pub fn update(d: *Self, b: []const u8) void { - 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; - @memcpy(d.buf[d.buf_len..][0..off], 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..][0..64]); - } - - // Copy any remainder for next pass. - @memcpy(d.buf[d.buf_len..][0 .. b.len - off], b[off..]); - d.buf_len += @as(u8, @intCast(b[off..].len)); - - d.total_len += b.len; - } - - pub fn peek(d: Self) [digest_length]u8 { - var copy = d; - return copy.finalResult(); - } - - pub fn final(d: *Self, out: *[digest_length]u8) void { - // The buffer here will never be completely full. - @memset(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..]); - @memset(d.buf[0..], 0); - } - - // Append message length. - var i: usize = 1; - var len = d.total_len >> 5; - d.buf[63] = @as(u8, @intCast(d.total_len & 0x1f)) << 3; - while (i < 8) : (i += 1) { - d.buf[63 - i] = @as(u8, @intCast(len & 0xff)); - len >>= 8; - } - - d.round(d.buf[0..]); - - for (d.s, 0..) |s, j| { - mem.writeInt(u32, out[4 * j ..][0..4], s, .big); - } - } - - pub fn finalResult(d: *Self) [digest_length]u8 { - var result: [digest_length]u8 = undefined; - d.final(&result); - return result; - } - - fn round(d: *Self, b: *const [64]u8) void { - 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{ - roundParam(0, 1, 2, 3, 4, 0), - roundParam(4, 0, 1, 2, 3, 1), - roundParam(3, 4, 0, 1, 2, 2), - roundParam(2, 3, 4, 0, 1, 3), - roundParam(1, 2, 3, 4, 0, 4), - roundParam(0, 1, 2, 3, 4, 5), - roundParam(4, 0, 1, 2, 3, 6), - roundParam(3, 4, 0, 1, 2, 7), - roundParam(2, 3, 4, 0, 1, 8), - roundParam(1, 2, 3, 4, 0, 9), - roundParam(0, 1, 2, 3, 4, 10), - roundParam(4, 0, 1, 2, 3, 11), - roundParam(3, 4, 0, 1, 2, 12), - roundParam(2, 3, 4, 0, 1, 13), - roundParam(1, 2, 3, 4, 0, 14), - roundParam(0, 1, 2, 3, 4, 15), - }; - inline for (round0a) |r| { - s[r.i] = mem.readInt(u32, b[r.i * 4 ..][0..4], .big); - - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(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], @as(u32, 30)); - } - - const round0b = comptime [_]RoundParam{ - roundParam(4, 0, 1, 2, 3, 16), - roundParam(3, 4, 0, 1, 2, 17), - roundParam(2, 3, 4, 0, 1, 18), - roundParam(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, @as(u32, 1)); - - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(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], @as(u32, 30)); - } - - const round1 = comptime [_]RoundParam{ - roundParam(0, 1, 2, 3, 4, 20), - roundParam(4, 0, 1, 2, 3, 21), - roundParam(3, 4, 0, 1, 2, 22), - roundParam(2, 3, 4, 0, 1, 23), - roundParam(1, 2, 3, 4, 0, 24), - roundParam(0, 1, 2, 3, 4, 25), - roundParam(4, 0, 1, 2, 3, 26), - roundParam(3, 4, 0, 1, 2, 27), - roundParam(2, 3, 4, 0, 1, 28), - roundParam(1, 2, 3, 4, 0, 29), - roundParam(0, 1, 2, 3, 4, 30), - roundParam(4, 0, 1, 2, 3, 31), - roundParam(3, 4, 0, 1, 2, 32), - roundParam(2, 3, 4, 0, 1, 33), - roundParam(1, 2, 3, 4, 0, 34), - roundParam(0, 1, 2, 3, 4, 35), - roundParam(4, 0, 1, 2, 3, 36), - roundParam(3, 4, 0, 1, 2, 37), - roundParam(2, 3, 4, 0, 1, 38), - roundParam(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, @as(u32, 1)); - - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(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], @as(u32, 30)); - } - - const round2 = comptime [_]RoundParam{ - roundParam(0, 1, 2, 3, 4, 40), - roundParam(4, 0, 1, 2, 3, 41), - roundParam(3, 4, 0, 1, 2, 42), - roundParam(2, 3, 4, 0, 1, 43), - roundParam(1, 2, 3, 4, 0, 44), - roundParam(0, 1, 2, 3, 4, 45), - roundParam(4, 0, 1, 2, 3, 46), - roundParam(3, 4, 0, 1, 2, 47), - roundParam(2, 3, 4, 0, 1, 48), - roundParam(1, 2, 3, 4, 0, 49), - roundParam(0, 1, 2, 3, 4, 50), - roundParam(4, 0, 1, 2, 3, 51), - roundParam(3, 4, 0, 1, 2, 52), - roundParam(2, 3, 4, 0, 1, 53), - roundParam(1, 2, 3, 4, 0, 54), - roundParam(0, 1, 2, 3, 4, 55), - roundParam(4, 0, 1, 2, 3, 56), - roundParam(3, 4, 0, 1, 2, 57), - roundParam(2, 3, 4, 0, 1, 58), - roundParam(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, @as(u32, 1)); - - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(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], @as(u32, 30)); - } - - const round3 = comptime [_]RoundParam{ - roundParam(0, 1, 2, 3, 4, 60), - roundParam(4, 0, 1, 2, 3, 61), - roundParam(3, 4, 0, 1, 2, 62), - roundParam(2, 3, 4, 0, 1, 63), - roundParam(1, 2, 3, 4, 0, 64), - roundParam(0, 1, 2, 3, 4, 65), - roundParam(4, 0, 1, 2, 3, 66), - roundParam(3, 4, 0, 1, 2, 67), - roundParam(2, 3, 4, 0, 1, 68), - roundParam(1, 2, 3, 4, 0, 69), - roundParam(0, 1, 2, 3, 4, 70), - roundParam(4, 0, 1, 2, 3, 71), - roundParam(3, 4, 0, 1, 2, 72), - roundParam(2, 3, 4, 0, 1, 73), - roundParam(1, 2, 3, 4, 0, 74), - roundParam(0, 1, 2, 3, 4, 75), - roundParam(4, 0, 1, 2, 3, 76), - roundParam(3, 4, 0, 1, 2, 77), - roundParam(2, 3, 4, 0, 1, 78), - roundParam(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, @as(u32, 1)); - - v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], @as(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], @as(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]; - } - - pub const Error = error{}; - pub const Writer = std.io.GenericWriter(*Self, Error, write); - - fn write(self: *Self, bytes: []const u8) Error!usize { - self.update(bytes); - return bytes.len; - } - - pub fn writer(self: *Self) Writer { - return .{ .context = self }; - } -}; - -const htest = @import("test.zig"); - -test "sha1 single" { - try htest.assertEqualHash(Sha1, "da39a3ee5e6b4b0d3255bfef95601890afd80709", ""); - try htest.assertEqualHash(Sha1, "a9993e364706816aba3e25717850c26c9cd0d89d", "abc"); - try htest.assertEqualHash(Sha1, "a49b2446a02c645bf419f995b67091253a04a259", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); -} - -test "sha1 streaming" { - var h = Sha1.init(.{}); - var out: [20]u8 = undefined; - - h.final(&out); - try htest.assertEqual("da39a3ee5e6b4b0d3255bfef95601890afd80709", out[0..]); - - h = Sha1.init(.{}); - h.update("abc"); - h.final(&out); - try htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]); - - h = Sha1.init(.{}); - h.update("a"); - h.update("b"); - h.update("c"); - h.final(&out); - try htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]); -} - -test "sha1 aligned final" { - var block = [_]u8{0} ** Sha1.block_length; - var out: [Sha1.digest_length]u8 = undefined; - - var h = Sha1.init(.{}); - h.update(&block); - h.final(out[0..]); -}