authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-10 17:09:21-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-14 00:14:21-07:00
log96a4e9b866ee899fcae164693a47b68d18f56257
treef11a31331b2c071a462fc693e72e03cd0ef347b4
parent5a8acc91158e84d1478478cbf6aa510091708b93

std.crypto: fix Sha1 namespace


3 files changed, 308 insertions(+), 323 deletions(-)

lib/std/crypto.zig+2-4
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1//! Cryptography.1//! Cryptography.
22
3const root = @import("root");3const std = @import("std.zig");
44
5pub const timing_safe = @import("crypto/timing_safe.zig");5pub const timing_safe = @import("crypto/timing_safe.zig");
66
...@@ -118,7 +118,7 @@ pub const hash = struct {...@@ -118,7 +118,7 @@ pub const hash = struct {
118 pub const blake2 = @import("crypto/blake2.zig");118 pub const blake2 = @import("crypto/blake2.zig");
119 pub const Blake3 = @import("crypto/blake3.zig").Blake3;119 pub const Blake3 = @import("crypto/blake3.zig").Blake3;
120 pub const Md5 = @import("crypto/md5.zig").Md5;120 pub const Md5 = @import("crypto/md5.zig").Md5;
121 pub const Sha1 = @import("crypto/sha1.zig").Sha1;121 pub const Sha1 = @import("crypto/Sha1.zig");
122 pub const sha2 = @import("crypto/sha2.zig");122 pub const sha2 = @import("crypto/sha2.zig");
123 pub const sha3 = @import("crypto/sha3.zig");123 pub const sha3 = @import("crypto/sha3.zig");
124 pub const composition = @import("crypto/hash_composition.zig");124 pub const composition = @import("crypto/hash_composition.zig");
...@@ -216,8 +216,6 @@ pub const random = @import("crypto/tlcsprng.zig").interface;...@@ -216,8 +216,6 @@ pub const random = @import("crypto/tlcsprng.zig").interface;
216/// Encoding and decoding216/// Encoding and decoding
217pub const codecs = @import("crypto/codecs.zig");217pub const codecs = @import("crypto/codecs.zig");
218218
219const std = @import("std.zig");
220
221pub const errors = @import("crypto/errors.zig");219pub const errors = @import("crypto/errors.zig");
222220
223pub const tls = @import("crypto/tls.zig");221pub const tls = @import("crypto/tls.zig");
lib/std/crypto/Sha1.zig created+306
...@@ -0,0 +1,306 @@
1//! The SHA-1 function is now considered cryptographically broken.
2//! Namely, it is feasible to find multiple inputs producing the same hash.
3//! For a fast-performing, cryptographically secure hash function, see SHA512/256, BLAKE2 or BLAKE3.
4
5const std = @import("../std.zig");
6const mem = std.mem;
7const math = std.math;
8const Sha1 = @This();
9
10pub const block_length = 64;
11pub const digest_length = 20;
12pub const Options = struct {};
13
14s: [5]u32,
15/// Streaming Cache
16buf: [64]u8 = undefined,
17buf_len: u8 = 0,
18total_len: u64 = 0,
19
20pub fn init(options: Options) Sha1 {
21 _ = options;
22 return .{
23 .s = [_]u32{
24 0x67452301,
25 0xEFCDAB89,
26 0x98BADCFE,
27 0x10325476,
28 0xC3D2E1F0,
29 },
30 };
31}
32
33pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {
34 var d = Sha1.init(options);
35 d.update(b);
36 d.final(out);
37}
38
39pub fn update(d: *Sha1, b: []const u8) void {
40 var off: usize = 0;
41
42 // Partial buffer exists from previous update. Copy into buffer then hash.
43 if (d.buf_len != 0 and d.buf_len + b.len >= 64) {
44 off += 64 - d.buf_len;
45 @memcpy(d.buf[d.buf_len..][0..off], b[0..off]);
46
47 d.round(d.buf[0..]);
48 d.buf_len = 0;
49 }
50
51 // Full middle blocks.
52 while (off + 64 <= b.len) : (off += 64) {
53 d.round(b[off..][0..64]);
54 }
55
56 // Copy any remainder for next pass.
57 @memcpy(d.buf[d.buf_len..][0 .. b.len - off], b[off..]);
58 d.buf_len += @as(u8, @intCast(b[off..].len));
59
60 d.total_len += b.len;
61}
62
63pub fn peek(d: Sha1) [digest_length]u8 {
64 var copy = d;
65 return copy.finalResult();
66}
67
68pub fn final(d: *Sha1, out: *[digest_length]u8) void {
69 // The buffer here will never be completely full.
70 @memset(d.buf[d.buf_len..], 0);
71
72 // Append padding bits.
73 d.buf[d.buf_len] = 0x80;
74 d.buf_len += 1;
75
76 // > 448 mod 512 so need to add an extra round to wrap around.
77 if (64 - d.buf_len < 8) {
78 d.round(d.buf[0..]);
79 @memset(d.buf[0..], 0);
80 }
81
82 // Append message length.
83 var i: usize = 1;
84 var len = d.total_len >> 5;
85 d.buf[63] = @as(u8, @intCast(d.total_len & 0x1f)) << 3;
86 while (i < 8) : (i += 1) {
87 d.buf[63 - i] = @as(u8, @intCast(len & 0xff));
88 len >>= 8;
89 }
90
91 d.round(d.buf[0..]);
92
93 for (d.s, 0..) |s, j| {
94 mem.writeInt(u32, out[4 * j ..][0..4], s, .big);
95 }
96}
97
98pub fn finalResult(d: *Sha1) [digest_length]u8 {
99 var result: [digest_length]u8 = undefined;
100 d.final(&result);
101 return result;
102}
103
104fn round(d: *Sha1, b: *const [64]u8) void {
105 var s: [16]u32 = undefined;
106
107 var v: [5]u32 = [_]u32{
108 d.s[0],
109 d.s[1],
110 d.s[2],
111 d.s[3],
112 d.s[4],
113 };
114
115 const round0a = comptime [_]RoundParam{
116 .abcdei(0, 1, 2, 3, 4, 0),
117 .abcdei(4, 0, 1, 2, 3, 1),
118 .abcdei(3, 4, 0, 1, 2, 2),
119 .abcdei(2, 3, 4, 0, 1, 3),
120 .abcdei(1, 2, 3, 4, 0, 4),
121 .abcdei(0, 1, 2, 3, 4, 5),
122 .abcdei(4, 0, 1, 2, 3, 6),
123 .abcdei(3, 4, 0, 1, 2, 7),
124 .abcdei(2, 3, 4, 0, 1, 8),
125 .abcdei(1, 2, 3, 4, 0, 9),
126 .abcdei(0, 1, 2, 3, 4, 10),
127 .abcdei(4, 0, 1, 2, 3, 11),
128 .abcdei(3, 4, 0, 1, 2, 12),
129 .abcdei(2, 3, 4, 0, 1, 13),
130 .abcdei(1, 2, 3, 4, 0, 14),
131 .abcdei(0, 1, 2, 3, 4, 15),
132 };
133 inline for (round0a) |r| {
134 s[r.i] = mem.readInt(u32, b[r.i * 4 ..][0..4], .big);
135
136 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]));
137 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
138 }
139
140 const round0b = comptime [_]RoundParam{
141 .abcdei(4, 0, 1, 2, 3, 16),
142 .abcdei(3, 4, 0, 1, 2, 17),
143 .abcdei(2, 3, 4, 0, 1, 18),
144 .abcdei(1, 2, 3, 4, 0, 19),
145 };
146 inline for (round0b) |r| {
147 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
148 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
149
150 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]));
151 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
152 }
153
154 const round1 = comptime [_]RoundParam{
155 .abcdei(0, 1, 2, 3, 4, 20),
156 .abcdei(4, 0, 1, 2, 3, 21),
157 .abcdei(3, 4, 0, 1, 2, 22),
158 .abcdei(2, 3, 4, 0, 1, 23),
159 .abcdei(1, 2, 3, 4, 0, 24),
160 .abcdei(0, 1, 2, 3, 4, 25),
161 .abcdei(4, 0, 1, 2, 3, 26),
162 .abcdei(3, 4, 0, 1, 2, 27),
163 .abcdei(2, 3, 4, 0, 1, 28),
164 .abcdei(1, 2, 3, 4, 0, 29),
165 .abcdei(0, 1, 2, 3, 4, 30),
166 .abcdei(4, 0, 1, 2, 3, 31),
167 .abcdei(3, 4, 0, 1, 2, 32),
168 .abcdei(2, 3, 4, 0, 1, 33),
169 .abcdei(1, 2, 3, 4, 0, 34),
170 .abcdei(0, 1, 2, 3, 4, 35),
171 .abcdei(4, 0, 1, 2, 3, 36),
172 .abcdei(3, 4, 0, 1, 2, 37),
173 .abcdei(2, 3, 4, 0, 1, 38),
174 .abcdei(1, 2, 3, 4, 0, 39),
175 };
176 inline for (round1) |r| {
177 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
178 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
179
180 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]);
181 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
182 }
183
184 const round2 = comptime [_]RoundParam{
185 .abcdei(0, 1, 2, 3, 4, 40),
186 .abcdei(4, 0, 1, 2, 3, 41),
187 .abcdei(3, 4, 0, 1, 2, 42),
188 .abcdei(2, 3, 4, 0, 1, 43),
189 .abcdei(1, 2, 3, 4, 0, 44),
190 .abcdei(0, 1, 2, 3, 4, 45),
191 .abcdei(4, 0, 1, 2, 3, 46),
192 .abcdei(3, 4, 0, 1, 2, 47),
193 .abcdei(2, 3, 4, 0, 1, 48),
194 .abcdei(1, 2, 3, 4, 0, 49),
195 .abcdei(0, 1, 2, 3, 4, 50),
196 .abcdei(4, 0, 1, 2, 3, 51),
197 .abcdei(3, 4, 0, 1, 2, 52),
198 .abcdei(2, 3, 4, 0, 1, 53),
199 .abcdei(1, 2, 3, 4, 0, 54),
200 .abcdei(0, 1, 2, 3, 4, 55),
201 .abcdei(4, 0, 1, 2, 3, 56),
202 .abcdei(3, 4, 0, 1, 2, 57),
203 .abcdei(2, 3, 4, 0, 1, 58),
204 .abcdei(1, 2, 3, 4, 0, 59),
205 };
206 inline for (round2) |r| {
207 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
208 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
209
210 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]));
211 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
212 }
213
214 const round3 = comptime [_]RoundParam{
215 .abcdei(0, 1, 2, 3, 4, 60),
216 .abcdei(4, 0, 1, 2, 3, 61),
217 .abcdei(3, 4, 0, 1, 2, 62),
218 .abcdei(2, 3, 4, 0, 1, 63),
219 .abcdei(1, 2, 3, 4, 0, 64),
220 .abcdei(0, 1, 2, 3, 4, 65),
221 .abcdei(4, 0, 1, 2, 3, 66),
222 .abcdei(3, 4, 0, 1, 2, 67),
223 .abcdei(2, 3, 4, 0, 1, 68),
224 .abcdei(1, 2, 3, 4, 0, 69),
225 .abcdei(0, 1, 2, 3, 4, 70),
226 .abcdei(4, 0, 1, 2, 3, 71),
227 .abcdei(3, 4, 0, 1, 2, 72),
228 .abcdei(2, 3, 4, 0, 1, 73),
229 .abcdei(1, 2, 3, 4, 0, 74),
230 .abcdei(0, 1, 2, 3, 4, 75),
231 .abcdei(4, 0, 1, 2, 3, 76),
232 .abcdei(3, 4, 0, 1, 2, 77),
233 .abcdei(2, 3, 4, 0, 1, 78),
234 .abcdei(1, 2, 3, 4, 0, 79),
235 };
236 inline for (round3) |r| {
237 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
238 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
239
240 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]);
241 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
242 }
243
244 d.s[0] +%= v[0];
245 d.s[1] +%= v[1];
246 d.s[2] +%= v[2];
247 d.s[3] +%= v[3];
248 d.s[4] +%= v[4];
249}
250
251const RoundParam = struct {
252 a: usize,
253 b: usize,
254 c: usize,
255 d: usize,
256 e: usize,
257 i: u32,
258
259 fn abcdei(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam {
260 return .{
261 .a = a,
262 .b = b,
263 .c = c,
264 .d = d,
265 .e = e,
266 .i = i,
267 };
268 }
269};
270
271const htest = @import("test.zig");
272
273test "sha1 single" {
274 try htest.assertEqualHash(Sha1, "da39a3ee5e6b4b0d3255bfef95601890afd80709", "");
275 try htest.assertEqualHash(Sha1, "a9993e364706816aba3e25717850c26c9cd0d89d", "abc");
276 try htest.assertEqualHash(Sha1, "a49b2446a02c645bf419f995b67091253a04a259", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
277}
278
279test "sha1 streaming" {
280 var h = Sha1.init(.{});
281 var out: [20]u8 = undefined;
282
283 h.final(&out);
284 try htest.assertEqual("da39a3ee5e6b4b0d3255bfef95601890afd80709", out[0..]);
285
286 h = Sha1.init(.{});
287 h.update("abc");
288 h.final(&out);
289 try htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]);
290
291 h = Sha1.init(.{});
292 h.update("a");
293 h.update("b");
294 h.update("c");
295 h.final(&out);
296 try htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]);
297}
298
299test "sha1 aligned final" {
300 var block = [_]u8{0} ** Sha1.block_length;
301 var out: [Sha1.digest_length]u8 = undefined;
302
303 var h = Sha1.init(.{});
304 h.update(&block);
305 h.final(out[0..]);
306}
lib/std/crypto/sha1.zig deleted-319
...@@ -1,319 +0,0 @@
1const std = @import("../std.zig");
2const mem = std.mem;
3const math = std.math;
4
5const RoundParam = struct {
6 a: usize,
7 b: usize,
8 c: usize,
9 d: usize,
10 e: usize,
11 i: u32,
12};
13
14fn roundParam(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam {
15 return RoundParam{
16 .a = a,
17 .b = b,
18 .c = c,
19 .d = d,
20 .e = e,
21 .i = i,
22 };
23}
24
25/// The SHA-1 function is now considered cryptographically broken.
26/// Namely, it is feasible to find multiple inputs producing the same hash.
27/// For a fast-performing, cryptographically secure hash function, see SHA512/256, BLAKE2 or BLAKE3.
28pub const Sha1 = struct {
29 const Self = @This();
30 pub const block_length = 64;
31 pub const digest_length = 20;
32 pub const Options = struct {};
33
34 s: [5]u32,
35 // Streaming Cache
36 buf: [64]u8 = undefined,
37 buf_len: u8 = 0,
38 total_len: u64 = 0,
39
40 pub fn init(options: Options) Self {
41 _ = options;
42 return Self{
43 .s = [_]u32{
44 0x67452301,
45 0xEFCDAB89,
46 0x98BADCFE,
47 0x10325476,
48 0xC3D2E1F0,
49 },
50 };
51 }
52
53 pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {
54 var d = Sha1.init(options);
55 d.update(b);
56 d.final(out);
57 }
58
59 pub fn update(d: *Self, b: []const u8) void {
60 var off: usize = 0;
61
62 // Partial buffer exists from previous update. Copy into buffer then hash.
63 if (d.buf_len != 0 and d.buf_len + b.len >= 64) {
64 off += 64 - d.buf_len;
65 @memcpy(d.buf[d.buf_len..][0..off], b[0..off]);
66
67 d.round(d.buf[0..]);
68 d.buf_len = 0;
69 }
70
71 // Full middle blocks.
72 while (off + 64 <= b.len) : (off += 64) {
73 d.round(b[off..][0..64]);
74 }
75
76 // Copy any remainder for next pass.
77 @memcpy(d.buf[d.buf_len..][0 .. b.len - off], b[off..]);
78 d.buf_len += @as(u8, @intCast(b[off..].len));
79
80 d.total_len += b.len;
81 }
82
83 pub fn peek(d: Self) [digest_length]u8 {
84 var copy = d;
85 return copy.finalResult();
86 }
87
88 pub fn final(d: *Self, out: *[digest_length]u8) void {
89 // The buffer here will never be completely full.
90 @memset(d.buf[d.buf_len..], 0);
91
92 // Append padding bits.
93 d.buf[d.buf_len] = 0x80;
94 d.buf_len += 1;
95
96 // > 448 mod 512 so need to add an extra round to wrap around.
97 if (64 - d.buf_len < 8) {
98 d.round(d.buf[0..]);
99 @memset(d.buf[0..], 0);
100 }
101
102 // Append message length.
103 var i: usize = 1;
104 var len = d.total_len >> 5;
105 d.buf[63] = @as(u8, @intCast(d.total_len & 0x1f)) << 3;
106 while (i < 8) : (i += 1) {
107 d.buf[63 - i] = @as(u8, @intCast(len & 0xff));
108 len >>= 8;
109 }
110
111 d.round(d.buf[0..]);
112
113 for (d.s, 0..) |s, j| {
114 mem.writeInt(u32, out[4 * j ..][0..4], s, .big);
115 }
116 }
117
118 pub fn finalResult(d: *Self) [digest_length]u8 {
119 var result: [digest_length]u8 = undefined;
120 d.final(&result);
121 return result;
122 }
123
124 fn round(d: *Self, b: *const [64]u8) void {
125 var s: [16]u32 = undefined;
126
127 var v: [5]u32 = [_]u32{
128 d.s[0],
129 d.s[1],
130 d.s[2],
131 d.s[3],
132 d.s[4],
133 };
134
135 const round0a = comptime [_]RoundParam{
136 roundParam(0, 1, 2, 3, 4, 0),
137 roundParam(4, 0, 1, 2, 3, 1),
138 roundParam(3, 4, 0, 1, 2, 2),
139 roundParam(2, 3, 4, 0, 1, 3),
140 roundParam(1, 2, 3, 4, 0, 4),
141 roundParam(0, 1, 2, 3, 4, 5),
142 roundParam(4, 0, 1, 2, 3, 6),
143 roundParam(3, 4, 0, 1, 2, 7),
144 roundParam(2, 3, 4, 0, 1, 8),
145 roundParam(1, 2, 3, 4, 0, 9),
146 roundParam(0, 1, 2, 3, 4, 10),
147 roundParam(4, 0, 1, 2, 3, 11),
148 roundParam(3, 4, 0, 1, 2, 12),
149 roundParam(2, 3, 4, 0, 1, 13),
150 roundParam(1, 2, 3, 4, 0, 14),
151 roundParam(0, 1, 2, 3, 4, 15),
152 };
153 inline for (round0a) |r| {
154 s[r.i] = mem.readInt(u32, b[r.i * 4 ..][0..4], .big);
155
156 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]));
157 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
158 }
159
160 const round0b = comptime [_]RoundParam{
161 roundParam(4, 0, 1, 2, 3, 16),
162 roundParam(3, 4, 0, 1, 2, 17),
163 roundParam(2, 3, 4, 0, 1, 18),
164 roundParam(1, 2, 3, 4, 0, 19),
165 };
166 inline for (round0b) |r| {
167 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
168 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
169
170 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]));
171 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
172 }
173
174 const round1 = comptime [_]RoundParam{
175 roundParam(0, 1, 2, 3, 4, 20),
176 roundParam(4, 0, 1, 2, 3, 21),
177 roundParam(3, 4, 0, 1, 2, 22),
178 roundParam(2, 3, 4, 0, 1, 23),
179 roundParam(1, 2, 3, 4, 0, 24),
180 roundParam(0, 1, 2, 3, 4, 25),
181 roundParam(4, 0, 1, 2, 3, 26),
182 roundParam(3, 4, 0, 1, 2, 27),
183 roundParam(2, 3, 4, 0, 1, 28),
184 roundParam(1, 2, 3, 4, 0, 29),
185 roundParam(0, 1, 2, 3, 4, 30),
186 roundParam(4, 0, 1, 2, 3, 31),
187 roundParam(3, 4, 0, 1, 2, 32),
188 roundParam(2, 3, 4, 0, 1, 33),
189 roundParam(1, 2, 3, 4, 0, 34),
190 roundParam(0, 1, 2, 3, 4, 35),
191 roundParam(4, 0, 1, 2, 3, 36),
192 roundParam(3, 4, 0, 1, 2, 37),
193 roundParam(2, 3, 4, 0, 1, 38),
194 roundParam(1, 2, 3, 4, 0, 39),
195 };
196 inline for (round1) |r| {
197 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
198 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
199
200 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]);
201 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
202 }
203
204 const round2 = comptime [_]RoundParam{
205 roundParam(0, 1, 2, 3, 4, 40),
206 roundParam(4, 0, 1, 2, 3, 41),
207 roundParam(3, 4, 0, 1, 2, 42),
208 roundParam(2, 3, 4, 0, 1, 43),
209 roundParam(1, 2, 3, 4, 0, 44),
210 roundParam(0, 1, 2, 3, 4, 45),
211 roundParam(4, 0, 1, 2, 3, 46),
212 roundParam(3, 4, 0, 1, 2, 47),
213 roundParam(2, 3, 4, 0, 1, 48),
214 roundParam(1, 2, 3, 4, 0, 49),
215 roundParam(0, 1, 2, 3, 4, 50),
216 roundParam(4, 0, 1, 2, 3, 51),
217 roundParam(3, 4, 0, 1, 2, 52),
218 roundParam(2, 3, 4, 0, 1, 53),
219 roundParam(1, 2, 3, 4, 0, 54),
220 roundParam(0, 1, 2, 3, 4, 55),
221 roundParam(4, 0, 1, 2, 3, 56),
222 roundParam(3, 4, 0, 1, 2, 57),
223 roundParam(2, 3, 4, 0, 1, 58),
224 roundParam(1, 2, 3, 4, 0, 59),
225 };
226 inline for (round2) |r| {
227 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
228 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
229
230 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]));
231 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
232 }
233
234 const round3 = comptime [_]RoundParam{
235 roundParam(0, 1, 2, 3, 4, 60),
236 roundParam(4, 0, 1, 2, 3, 61),
237 roundParam(3, 4, 0, 1, 2, 62),
238 roundParam(2, 3, 4, 0, 1, 63),
239 roundParam(1, 2, 3, 4, 0, 64),
240 roundParam(0, 1, 2, 3, 4, 65),
241 roundParam(4, 0, 1, 2, 3, 66),
242 roundParam(3, 4, 0, 1, 2, 67),
243 roundParam(2, 3, 4, 0, 1, 68),
244 roundParam(1, 2, 3, 4, 0, 69),
245 roundParam(0, 1, 2, 3, 4, 70),
246 roundParam(4, 0, 1, 2, 3, 71),
247 roundParam(3, 4, 0, 1, 2, 72),
248 roundParam(2, 3, 4, 0, 1, 73),
249 roundParam(1, 2, 3, 4, 0, 74),
250 roundParam(0, 1, 2, 3, 4, 75),
251 roundParam(4, 0, 1, 2, 3, 76),
252 roundParam(3, 4, 0, 1, 2, 77),
253 roundParam(2, 3, 4, 0, 1, 78),
254 roundParam(1, 2, 3, 4, 0, 79),
255 };
256 inline for (round3) |r| {
257 const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
258 s[r.i & 0xf] = math.rotl(u32, t, @as(u32, 1));
259
260 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]);
261 v[r.b] = math.rotl(u32, v[r.b], @as(u32, 30));
262 }
263
264 d.s[0] +%= v[0];
265 d.s[1] +%= v[1];
266 d.s[2] +%= v[2];
267 d.s[3] +%= v[3];
268 d.s[4] +%= v[4];
269 }
270
271 pub const Error = error{};
272 pub const Writer = std.io.GenericWriter(*Self, Error, write);
273
274 fn write(self: *Self, bytes: []const u8) Error!usize {
275 self.update(bytes);
276 return bytes.len;
277 }
278
279 pub fn writer(self: *Self) Writer {
280 return .{ .context = self };
281 }
282};
283
284const htest = @import("test.zig");
285
286test "sha1 single" {
287 try htest.assertEqualHash(Sha1, "da39a3ee5e6b4b0d3255bfef95601890afd80709", "");
288 try htest.assertEqualHash(Sha1, "a9993e364706816aba3e25717850c26c9cd0d89d", "abc");
289 try htest.assertEqualHash(Sha1, "a49b2446a02c645bf419f995b67091253a04a259", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
290}
291
292test "sha1 streaming" {
293 var h = Sha1.init(.{});
294 var out: [20]u8 = undefined;
295
296 h.final(&out);
297 try htest.assertEqual("da39a3ee5e6b4b0d3255bfef95601890afd80709", out[0..]);
298
299 h = Sha1.init(.{});
300 h.update("abc");
301 h.final(&out);
302 try htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]);
303
304 h = Sha1.init(.{});
305 h.update("a");
306 h.update("b");
307 h.update("c");
308 h.final(&out);
309 try htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]);
310}
311
312test "sha1 aligned final" {
313 var block = [_]u8{0} ** Sha1.block_length;
314 var out: [Sha1.digest_length]u8 = undefined;
315
316 var h = Sha1.init(.{});
317 h.update(&block);
318 h.final(out[0..]);
319}