master
1//! Secure Hashing Algorithm 2 (SHA2)
2//!
3//! Published by the National Institute of Standards and Technology (NIST) [1] [2].
4//!
5//! Truncation mitigates length-extension attacks but increases vulnerability to collision
6//! attacks. Collision attacks remain impractical for all types defined here.
7//!
8//! T: original hash function, whose output is simply truncated.
9//! A truncated output is just the first bytes of a longer output.
10//! _: hash function with context separation.
11//! Different lengths produce completely different outputs.
12//!
13//! [1] https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
14//! [2] https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-208.pdf
15
16const std = @import("../std.zig");
17const builtin = @import("builtin");
18const mem = std.mem;
19const math = std.math;
20const htest = @import("test.zig");
21
22pub const Sha224 = Sha2x32(iv224, 224);
23pub const Sha256 = Sha2x32(iv256, 256);
24pub const Sha384 = Sha2x64(iv384, 384);
25pub const Sha512 = Sha2x64(iv512, 512);
26
27/// SHA-256 truncated to leftmost 192 bits.
28pub const Sha256T192 = Sha2x32(iv256, 192);
29
30/// SHA-512 truncated to leftmost 224 bits.
31pub const Sha512T224 = Sha2x64(iv512, 224);
32/// SHA-512 truncated to leftmost 256 bits.
33pub const Sha512T256 = Sha2x64(iv512, 256);
34
35/// SHA-512 with a different initialization vector truncated to leftmost 224 bits.
36pub const Sha512_224 = Sha2x64(truncatedSha512Iv(224), 224);
37/// SHA-512 with a different initialization vector truncated to leftmost 256 bits.
38pub const Sha512_256 = Sha2x64(truncatedSha512Iv(256), 256);
39
40/// Low 32 bits of iv384.
41const iv224 = Iv32{
42 0xC1059ED8,
43 0x367CD507,
44 0x3070DD17,
45 0xF70E5939,
46 0xFFC00B31,
47 0x68581511,
48 0x64F98FA7,
49 0xBEFA4FA4,
50};
51/// First thirty-two bits of the fractional parts of the square
52/// roots of the first eight prime numbers.
53const iv256 = Iv32{
54 0x6A09E667,
55 0xBB67AE85,
56 0x3C6EF372,
57 0xA54FF53A,
58 0x510E527F,
59 0x9B05688C,
60 0x1F83D9AB,
61 0x5BE0CD19,
62};
63
64/// First sixty-four bits of the fractional parts of the square
65/// roots of the ninth through sixteenth prime numbers.
66const iv384 = Iv64{
67 0xCBBB9D5DC1059ED8,
68 0x629A292A367CD507,
69 0x9159015A3070DD17,
70 0x152FECD8F70E5939,
71 0x67332667FFC00B31,
72 0x8EB44A8768581511,
73 0xDB0C2E0D64F98FA7,
74 0x47B5481DBEFA4FA4,
75};
76/// First sixty-four bits of the fractional parts of the square
77/// roots of the first eight prime numbers.
78const iv512 = Iv64{
79 0x6A09E667F3BCC908,
80 0xBB67AE8584CAA73B,
81 0x3C6EF372FE94F82B,
82 0xA54FF53A5F1D36F1,
83 0x510E527FADE682D1,
84 0x9B05688C2B3E6C1F,
85 0x1F83D9ABFB41BD6B,
86 0x5BE0CD19137E2179,
87};
88
89const Iv32 = [8]u32;
90fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type {
91 return struct {
92 const Self = @This();
93 pub const block_length = 64;
94 pub const digest_length = digest_bits / 8;
95 pub const Options = struct {};
96
97 s: [8]u32 align(16),
98 // Streaming Cache
99 buf: [64]u8 = undefined,
100 buf_len: u8 = 0,
101 total_len: u64 = 0,
102
103 pub fn init(options: Options) Self {
104 _ = options;
105 return Self{ .s = iv };
106 }
107
108 pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {
109 var d = Self.init(options);
110 d.update(b);
111 d.final(out);
112 }
113
114 pub fn update(d: *Self, b: []const u8) void {
115 var off: usize = 0;
116
117 // Partial buffer exists from previous update. Copy into buffer then hash.
118 if (d.buf_len != 0 and d.buf_len + b.len >= 64) {
119 off += 64 - d.buf_len;
120 @memcpy(d.buf[d.buf_len..][0..off], b[0..off]);
121
122 d.round(&d.buf);
123 d.buf_len = 0;
124 }
125
126 // Full middle blocks.
127 while (off + 64 <= b.len) : (off += 64) {
128 d.round(b[off..][0..64]);
129 }
130
131 // Copy any remainder for next pass.
132 const b_slice = b[off..];
133 @memcpy(d.buf[d.buf_len..][0..b_slice.len], b_slice);
134 d.buf_len += @as(u8, @intCast(b[off..].len));
135
136 d.total_len += b.len;
137 }
138
139 pub fn peek(d: Self) [digest_length]u8 {
140 var copy = d;
141 return copy.finalResult();
142 }
143
144 pub fn final(d: *Self, out: *[digest_length]u8) void {
145 // The buffer here will never be completely full.
146 @memset(d.buf[d.buf_len..], 0);
147
148 // Append padding bits.
149 d.buf[d.buf_len] = 0x80;
150 d.buf_len += 1;
151
152 // > 448 mod 512 so need to add an extra round to wrap around.
153 if (64 - d.buf_len < 8) {
154 d.round(&d.buf);
155 @memset(d.buf[0..], 0);
156 }
157
158 // Append message length.
159 var i: usize = 1;
160 var len = d.total_len >> 5;
161 d.buf[63] = @as(u8, @intCast(d.total_len & 0x1f)) << 3;
162 while (i < 8) : (i += 1) {
163 d.buf[63 - i] = @as(u8, @intCast(len & 0xff));
164 len >>= 8;
165 }
166
167 d.round(&d.buf);
168
169 // May truncate for possible 224 or 192 output
170 const rr = d.s[0 .. digest_length / 4];
171
172 for (rr, 0..) |s, j| {
173 mem.writeInt(u32, out[4 * j ..][0..4], s, .big);
174 }
175 }
176
177 pub fn finalResult(d: *Self) [digest_length]u8 {
178 var result: [digest_length]u8 = undefined;
179 d.final(&result);
180 return result;
181 }
182
183 const W = [64]u32{
184 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
185 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
186 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
187 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
188 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
189 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
190 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
191 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2,
192 };
193
194 fn round(d: *Self, b: *const [64]u8) void {
195 var s: [64]u32 align(16) = undefined;
196 for (@as(*align(1) const [16]u32, @ptrCast(b)), 0..) |*elem, i| {
197 s[i] = mem.readInt(u32, mem.asBytes(elem), .big);
198 }
199
200 if (!@inComptime()) {
201 const V4u32 = @Vector(4, u32);
202 switch (builtin.cpu.arch) {
203 .aarch64 => if (builtin.zig_backend != .stage2_c and comptime builtin.cpu.has(.aarch64, .sha2)) {
204 var x: V4u32 = d.s[0..4].*;
205 var y: V4u32 = d.s[4..8].*;
206 const s_v = @as(*[16]V4u32, @ptrCast(&s));
207
208 comptime var k: u8 = 0;
209 inline while (k < 16) : (k += 1) {
210 if (k > 3) {
211 s_v[k] = asm (
212 \\sha256su0.4s %[w0_3], %[w4_7]
213 \\sha256su1.4s %[w0_3], %[w8_11], %[w12_15]
214 : [w0_3] "=w" (-> V4u32),
215 : [_] "0" (s_v[k - 4]),
216 [w4_7] "w" (s_v[k - 3]),
217 [w8_11] "w" (s_v[k - 2]),
218 [w12_15] "w" (s_v[k - 1]),
219 );
220 }
221
222 const w: V4u32 = s_v[k] +% @as(V4u32, W[4 * k ..][0..4].*);
223 asm volatile (
224 \\mov.4s v0, %[x]
225 \\sha256h.4s %[x], %[y], %[w]
226 \\sha256h2.4s %[y], v0, %[w]
227 : [x] "=w" (x),
228 [y] "=w" (y),
229 : [_] "0" (x),
230 [_] "1" (y),
231 [w] "w" (w),
232 : .{ .v0 = true });
233 }
234
235 d.s[0..4].* = x +% @as(V4u32, d.s[0..4].*);
236 d.s[4..8].* = y +% @as(V4u32, d.s[4..8].*);
237 return;
238 },
239 // C backend doesn't currently support passing vectors to inline asm.
240 .x86_64 => if (builtin.zig_backend != .stage2_c and comptime builtin.cpu.hasAll(.x86, &.{ .sha, .avx2 })) {
241 var x: V4u32 = [_]u32{ d.s[5], d.s[4], d.s[1], d.s[0] };
242 var y: V4u32 = [_]u32{ d.s[7], d.s[6], d.s[3], d.s[2] };
243 const s_v = @as(*[16]V4u32, @ptrCast(&s));
244
245 comptime var k: u8 = 0;
246 inline while (k < 16) : (k += 1) {
247 if (k < 12) {
248 var tmp = s_v[k];
249 s_v[k + 4] = asm (
250 \\ sha256msg1 %[w4_7], %[tmp]
251 \\ vpalignr $0x4, %[w8_11], %[w12_15], %[result]
252 \\ paddd %[tmp], %[result]
253 \\ sha256msg2 %[w12_15], %[result]
254 : [tmp] "=&x" (tmp),
255 [result] "=&x" (-> V4u32),
256 : [_] "0" (tmp),
257 [w4_7] "x" (s_v[k + 1]),
258 [w8_11] "x" (s_v[k + 2]),
259 [w12_15] "x" (s_v[k + 3]),
260 );
261 }
262
263 const w: V4u32 = s_v[k] +% @as(V4u32, W[4 * k ..][0..4].*);
264 y = asm ("sha256rnds2 %[x], %[y]"
265 : [y] "=x" (-> V4u32),
266 : [_] "0" (y),
267 [x] "x" (x),
268 [_] "{xmm0}" (w),
269 );
270
271 x = asm ("sha256rnds2 %[y], %[x]"
272 : [x] "=x" (-> V4u32),
273 : [_] "0" (x),
274 [y] "x" (y),
275 [_] "{xmm0}" (@as(V4u32, @bitCast(@as(u128, @bitCast(w)) >> 64))),
276 );
277 }
278
279 d.s[0] +%= x[3];
280 d.s[1] +%= x[2];
281 d.s[4] +%= x[1];
282 d.s[5] +%= x[0];
283 d.s[2] +%= y[3];
284 d.s[3] +%= y[2];
285 d.s[6] +%= y[1];
286 d.s[7] +%= y[0];
287 return;
288 },
289 else => {},
290 }
291 }
292
293 var i: usize = 16;
294 while (i < 64) : (i += 1) {
295 s[i] = s[i - 16] +% s[i - 7] +% (math.rotr(u32, s[i - 15], @as(u32, 7)) ^ math.rotr(u32, s[i - 15], @as(u32, 18)) ^ (s[i - 15] >> 3)) +% (math.rotr(u32, s[i - 2], @as(u32, 17)) ^ math.rotr(u32, s[i - 2], @as(u32, 19)) ^ (s[i - 2] >> 10));
296 }
297
298 var v: [8]u32 = d.s;
299
300 const round0 = comptime [_]RoundParam256{
301 roundParam256(0, 1, 2, 3, 4, 5, 6, 7, 0),
302 roundParam256(7, 0, 1, 2, 3, 4, 5, 6, 1),
303 roundParam256(6, 7, 0, 1, 2, 3, 4, 5, 2),
304 roundParam256(5, 6, 7, 0, 1, 2, 3, 4, 3),
305 roundParam256(4, 5, 6, 7, 0, 1, 2, 3, 4),
306 roundParam256(3, 4, 5, 6, 7, 0, 1, 2, 5),
307 roundParam256(2, 3, 4, 5, 6, 7, 0, 1, 6),
308 roundParam256(1, 2, 3, 4, 5, 6, 7, 0, 7),
309 roundParam256(0, 1, 2, 3, 4, 5, 6, 7, 8),
310 roundParam256(7, 0, 1, 2, 3, 4, 5, 6, 9),
311 roundParam256(6, 7, 0, 1, 2, 3, 4, 5, 10),
312 roundParam256(5, 6, 7, 0, 1, 2, 3, 4, 11),
313 roundParam256(4, 5, 6, 7, 0, 1, 2, 3, 12),
314 roundParam256(3, 4, 5, 6, 7, 0, 1, 2, 13),
315 roundParam256(2, 3, 4, 5, 6, 7, 0, 1, 14),
316 roundParam256(1, 2, 3, 4, 5, 6, 7, 0, 15),
317 roundParam256(0, 1, 2, 3, 4, 5, 6, 7, 16),
318 roundParam256(7, 0, 1, 2, 3, 4, 5, 6, 17),
319 roundParam256(6, 7, 0, 1, 2, 3, 4, 5, 18),
320 roundParam256(5, 6, 7, 0, 1, 2, 3, 4, 19),
321 roundParam256(4, 5, 6, 7, 0, 1, 2, 3, 20),
322 roundParam256(3, 4, 5, 6, 7, 0, 1, 2, 21),
323 roundParam256(2, 3, 4, 5, 6, 7, 0, 1, 22),
324 roundParam256(1, 2, 3, 4, 5, 6, 7, 0, 23),
325 roundParam256(0, 1, 2, 3, 4, 5, 6, 7, 24),
326 roundParam256(7, 0, 1, 2, 3, 4, 5, 6, 25),
327 roundParam256(6, 7, 0, 1, 2, 3, 4, 5, 26),
328 roundParam256(5, 6, 7, 0, 1, 2, 3, 4, 27),
329 roundParam256(4, 5, 6, 7, 0, 1, 2, 3, 28),
330 roundParam256(3, 4, 5, 6, 7, 0, 1, 2, 29),
331 roundParam256(2, 3, 4, 5, 6, 7, 0, 1, 30),
332 roundParam256(1, 2, 3, 4, 5, 6, 7, 0, 31),
333 roundParam256(0, 1, 2, 3, 4, 5, 6, 7, 32),
334 roundParam256(7, 0, 1, 2, 3, 4, 5, 6, 33),
335 roundParam256(6, 7, 0, 1, 2, 3, 4, 5, 34),
336 roundParam256(5, 6, 7, 0, 1, 2, 3, 4, 35),
337 roundParam256(4, 5, 6, 7, 0, 1, 2, 3, 36),
338 roundParam256(3, 4, 5, 6, 7, 0, 1, 2, 37),
339 roundParam256(2, 3, 4, 5, 6, 7, 0, 1, 38),
340 roundParam256(1, 2, 3, 4, 5, 6, 7, 0, 39),
341 roundParam256(0, 1, 2, 3, 4, 5, 6, 7, 40),
342 roundParam256(7, 0, 1, 2, 3, 4, 5, 6, 41),
343 roundParam256(6, 7, 0, 1, 2, 3, 4, 5, 42),
344 roundParam256(5, 6, 7, 0, 1, 2, 3, 4, 43),
345 roundParam256(4, 5, 6, 7, 0, 1, 2, 3, 44),
346 roundParam256(3, 4, 5, 6, 7, 0, 1, 2, 45),
347 roundParam256(2, 3, 4, 5, 6, 7, 0, 1, 46),
348 roundParam256(1, 2, 3, 4, 5, 6, 7, 0, 47),
349 roundParam256(0, 1, 2, 3, 4, 5, 6, 7, 48),
350 roundParam256(7, 0, 1, 2, 3, 4, 5, 6, 49),
351 roundParam256(6, 7, 0, 1, 2, 3, 4, 5, 50),
352 roundParam256(5, 6, 7, 0, 1, 2, 3, 4, 51),
353 roundParam256(4, 5, 6, 7, 0, 1, 2, 3, 52),
354 roundParam256(3, 4, 5, 6, 7, 0, 1, 2, 53),
355 roundParam256(2, 3, 4, 5, 6, 7, 0, 1, 54),
356 roundParam256(1, 2, 3, 4, 5, 6, 7, 0, 55),
357 roundParam256(0, 1, 2, 3, 4, 5, 6, 7, 56),
358 roundParam256(7, 0, 1, 2, 3, 4, 5, 6, 57),
359 roundParam256(6, 7, 0, 1, 2, 3, 4, 5, 58),
360 roundParam256(5, 6, 7, 0, 1, 2, 3, 4, 59),
361 roundParam256(4, 5, 6, 7, 0, 1, 2, 3, 60),
362 roundParam256(3, 4, 5, 6, 7, 0, 1, 2, 61),
363 roundParam256(2, 3, 4, 5, 6, 7, 0, 1, 62),
364 roundParam256(1, 2, 3, 4, 5, 6, 7, 0, 63),
365 };
366 inline for (round0) |r| {
367 v[r.h] = v[r.h] +% (math.rotr(u32, v[r.e], @as(u32, 6)) ^ math.rotr(u32, v[r.e], @as(u32, 11)) ^ math.rotr(u32, v[r.e], @as(u32, 25))) +% (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +% W[r.i] +% s[r.i];
368
369 v[r.d] = v[r.d] +% v[r.h];
370
371 v[r.h] = v[r.h] +% (math.rotr(u32, v[r.a], @as(u32, 2)) ^ math.rotr(u32, v[r.a], @as(u32, 13)) ^ math.rotr(u32, v[r.a], @as(u32, 22))) +% ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c]));
372 }
373
374 for (&d.s, v) |*dv, vv| dv.* +%= vv;
375 }
376 };
377}
378
379const RoundParam256 = struct {
380 a: usize,
381 b: usize,
382 c: usize,
383 d: usize,
384 e: usize,
385 f: usize,
386 g: usize,
387 h: usize,
388 i: usize,
389};
390
391fn roundParam256(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize) RoundParam256 {
392 return RoundParam256{
393 .a = a,
394 .b = b,
395 .c = c,
396 .d = d,
397 .e = e,
398 .f = f,
399 .g = g,
400 .h = h,
401 .i = i,
402 };
403}
404
405test Sha224 {
406 try htest.assertEqualHash(Sha224, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", "");
407 try htest.assertEqualHash(Sha224, "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc");
408 try htest.assertEqualHash(Sha224, "c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
409}
410
411test "sha224 streaming" {
412 var h = Sha224.init(.{});
413 var out: [28]u8 = undefined;
414
415 h.final(out[0..]);
416 try htest.assertEqual("d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", out[0..]);
417
418 h = Sha224.init(.{});
419 h.update("abc");
420 h.final(out[0..]);
421 try htest.assertEqual("23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", out[0..]);
422
423 h = Sha224.init(.{});
424 h.update("a");
425 h.update("b");
426 h.update("c");
427 h.final(out[0..]);
428 try htest.assertEqual("23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", out[0..]);
429}
430
431test Sha256 {
432 try htest.assertEqualHash(Sha256, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "");
433 try htest.assertEqualHash(Sha256, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc");
434 try htest.assertEqualHash(Sha256, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
435}
436
437test Sha256T192 {
438 try htest.assertEqualHash(Sha256T192, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934c", "");
439 try htest.assertEqualHash(Sha256T192, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9c", "abc");
440 try htest.assertEqualHash(Sha256T192, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
441}
442
443test "sha256 streaming" {
444 var h = Sha256.init(.{});
445 var out: [32]u8 = undefined;
446
447 h.final(out[0..]);
448 try htest.assertEqual("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", out[0..]);
449
450 h = Sha256.init(.{});
451 h.update("abc");
452 h.final(out[0..]);
453 try htest.assertEqual("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", out[0..]);
454
455 h = Sha256.init(.{});
456 h.update("a");
457 h.update("b");
458 h.update("c");
459 h.final(out[0..]);
460 try htest.assertEqual("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", out[0..]);
461}
462
463test "sha256 aligned final" {
464 var block = [_]u8{0} ** Sha256.block_length;
465 var out: [Sha256.digest_length]u8 = undefined;
466
467 var h = Sha256.init(.{});
468 h.update(&block);
469 h.final(out[0..]);
470}
471
472const Iv64 = [8]u64;
473fn Sha2x64(comptime iv: Iv64, digest_bits: comptime_int) type {
474 return struct {
475 const Self = @This();
476 pub const block_length = 128;
477 pub const digest_length = std.math.divCeil(comptime_int, digest_bits, 8) catch unreachable;
478 pub const Options = struct {};
479
480 s: Iv64,
481 // Streaming Cache
482 buf: [128]u8 = undefined,
483 buf_len: u8 = 0,
484 total_len: u128 = 0,
485
486 pub fn init(options: Options) Self {
487 _ = options;
488 return Self{ .s = iv };
489 }
490
491 pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {
492 var d = Self.init(options);
493 d.update(b);
494 d.final(out);
495 }
496
497 pub fn update(d: *Self, b: []const u8) void {
498 var off: usize = 0;
499
500 // Partial buffer exists from previous update. Copy into buffer then hash.
501 if (d.buf_len != 0 and d.buf_len + b.len >= 128) {
502 off += 128 - d.buf_len;
503 @memcpy(d.buf[d.buf_len..][0..off], b[0..off]);
504
505 d.round(&d.buf);
506 d.buf_len = 0;
507 }
508
509 // Full middle blocks.
510 while (off + 128 <= b.len) : (off += 128) {
511 d.round(b[off..][0..128]);
512 }
513
514 // Copy any remainder for next pass.
515 const b_slice = b[off..];
516 @memcpy(d.buf[d.buf_len..][0..b_slice.len], b_slice);
517 d.buf_len += @as(u8, @intCast(b[off..].len));
518
519 d.total_len += b.len;
520 }
521
522 pub fn peek(d: Self) [digest_length]u8 {
523 var copy = d;
524 return copy.finalResult();
525 }
526
527 pub fn final(d: *Self, out: *[digest_length]u8) void {
528 // The buffer here will never be completely full.
529 @memset(d.buf[d.buf_len..], 0);
530
531 // Append padding bits.
532 d.buf[d.buf_len] = 0x80;
533 d.buf_len += 1;
534
535 // > 896 mod 1024 so need to add an extra round to wrap around.
536 if (128 - d.buf_len < 16) {
537 d.round(d.buf[0..]);
538 @memset(d.buf[0..], 0);
539 }
540
541 // Append message length.
542 var i: usize = 1;
543 var len = d.total_len >> 5;
544 d.buf[127] = @as(u8, @intCast(d.total_len & 0x1f)) << 3;
545 while (i < 16) : (i += 1) {
546 d.buf[127 - i] = @as(u8, @intCast(len & 0xff));
547 len >>= 8;
548 }
549
550 d.round(d.buf[0..]);
551
552 // May truncate for possible 384 output
553 const rr = d.s[0 .. digest_length / 8];
554
555 for (rr, 0..) |s, j| {
556 mem.writeInt(u64, out[8 * j ..][0..8], s, .big);
557 }
558
559 if (digest_bits % 8 != 0) @compileError("impl doesn't support non-byte digest_len");
560 const bytes_left = digest_bits / 8 % 8;
561 if (bytes_left > 0) {
562 const rest = d.s[(digest_bits / 64)];
563 var buf: [8]u8 = undefined;
564 std.mem.writeInt(u64, &buf, rest, .big);
565 @memcpy(out[digest_bits / 64 * 8 ..], buf[0..bytes_left]);
566 }
567 }
568
569 pub fn finalResult(d: *Self) [digest_length]u8 {
570 var result: [digest_length]u8 = undefined;
571 d.final(&result);
572 return result;
573 }
574
575 fn round(d: *Self, b: *const [128]u8) void {
576 var s: [80]u64 = undefined;
577
578 var i: usize = 0;
579 while (i < 16) : (i += 1) {
580 s[i] = mem.readInt(u64, b[i * 8 ..][0..8], .big);
581 }
582 while (i < 80) : (i += 1) {
583 s[i] = s[i - 16] +% s[i - 7] +%
584 (math.rotr(u64, s[i - 15], @as(u64, 1)) ^ math.rotr(u64, s[i - 15], @as(u64, 8)) ^ (s[i - 15] >> 7)) +%
585 (math.rotr(u64, s[i - 2], @as(u64, 19)) ^ math.rotr(u64, s[i - 2], @as(u64, 61)) ^ (s[i - 2] >> 6));
586 }
587
588 var v: [8]u64 = d.s;
589
590 const round0 = comptime [_]RoundParam512{
591 roundParam512(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98D728AE22),
592 roundParam512(7, 0, 1, 2, 3, 4, 5, 6, 1, 0x7137449123EF65CD),
593 roundParam512(6, 7, 0, 1, 2, 3, 4, 5, 2, 0xB5C0FBCFEC4D3B2F),
594 roundParam512(5, 6, 7, 0, 1, 2, 3, 4, 3, 0xE9B5DBA58189DBBC),
595 roundParam512(4, 5, 6, 7, 0, 1, 2, 3, 4, 0x3956C25BF348B538),
596 roundParam512(3, 4, 5, 6, 7, 0, 1, 2, 5, 0x59F111F1B605D019),
597 roundParam512(2, 3, 4, 5, 6, 7, 0, 1, 6, 0x923F82A4AF194F9B),
598 roundParam512(1, 2, 3, 4, 5, 6, 7, 0, 7, 0xAB1C5ED5DA6D8118),
599 roundParam512(0, 1, 2, 3, 4, 5, 6, 7, 8, 0xD807AA98A3030242),
600 roundParam512(7, 0, 1, 2, 3, 4, 5, 6, 9, 0x12835B0145706FBE),
601 roundParam512(6, 7, 0, 1, 2, 3, 4, 5, 10, 0x243185BE4EE4B28C),
602 roundParam512(5, 6, 7, 0, 1, 2, 3, 4, 11, 0x550C7DC3D5FFB4E2),
603 roundParam512(4, 5, 6, 7, 0, 1, 2, 3, 12, 0x72BE5D74F27B896F),
604 roundParam512(3, 4, 5, 6, 7, 0, 1, 2, 13, 0x80DEB1FE3B1696B1),
605 roundParam512(2, 3, 4, 5, 6, 7, 0, 1, 14, 0x9BDC06A725C71235),
606 roundParam512(1, 2, 3, 4, 5, 6, 7, 0, 15, 0xC19BF174CF692694),
607 roundParam512(0, 1, 2, 3, 4, 5, 6, 7, 16, 0xE49B69C19EF14AD2),
608 roundParam512(7, 0, 1, 2, 3, 4, 5, 6, 17, 0xEFBE4786384F25E3),
609 roundParam512(6, 7, 0, 1, 2, 3, 4, 5, 18, 0x0FC19DC68B8CD5B5),
610 roundParam512(5, 6, 7, 0, 1, 2, 3, 4, 19, 0x240CA1CC77AC9C65),
611 roundParam512(4, 5, 6, 7, 0, 1, 2, 3, 20, 0x2DE92C6F592B0275),
612 roundParam512(3, 4, 5, 6, 7, 0, 1, 2, 21, 0x4A7484AA6EA6E483),
613 roundParam512(2, 3, 4, 5, 6, 7, 0, 1, 22, 0x5CB0A9DCBD41FBD4),
614 roundParam512(1, 2, 3, 4, 5, 6, 7, 0, 23, 0x76F988DA831153B5),
615 roundParam512(0, 1, 2, 3, 4, 5, 6, 7, 24, 0x983E5152EE66DFAB),
616 roundParam512(7, 0, 1, 2, 3, 4, 5, 6, 25, 0xA831C66D2DB43210),
617 roundParam512(6, 7, 0, 1, 2, 3, 4, 5, 26, 0xB00327C898FB213F),
618 roundParam512(5, 6, 7, 0, 1, 2, 3, 4, 27, 0xBF597FC7BEEF0EE4),
619 roundParam512(4, 5, 6, 7, 0, 1, 2, 3, 28, 0xC6E00BF33DA88FC2),
620 roundParam512(3, 4, 5, 6, 7, 0, 1, 2, 29, 0xD5A79147930AA725),
621 roundParam512(2, 3, 4, 5, 6, 7, 0, 1, 30, 0x06CA6351E003826F),
622 roundParam512(1, 2, 3, 4, 5, 6, 7, 0, 31, 0x142929670A0E6E70),
623 roundParam512(0, 1, 2, 3, 4, 5, 6, 7, 32, 0x27B70A8546D22FFC),
624 roundParam512(7, 0, 1, 2, 3, 4, 5, 6, 33, 0x2E1B21385C26C926),
625 roundParam512(6, 7, 0, 1, 2, 3, 4, 5, 34, 0x4D2C6DFC5AC42AED),
626 roundParam512(5, 6, 7, 0, 1, 2, 3, 4, 35, 0x53380D139D95B3DF),
627 roundParam512(4, 5, 6, 7, 0, 1, 2, 3, 36, 0x650A73548BAF63DE),
628 roundParam512(3, 4, 5, 6, 7, 0, 1, 2, 37, 0x766A0ABB3C77B2A8),
629 roundParam512(2, 3, 4, 5, 6, 7, 0, 1, 38, 0x81C2C92E47EDAEE6),
630 roundParam512(1, 2, 3, 4, 5, 6, 7, 0, 39, 0x92722C851482353B),
631 roundParam512(0, 1, 2, 3, 4, 5, 6, 7, 40, 0xA2BFE8A14CF10364),
632 roundParam512(7, 0, 1, 2, 3, 4, 5, 6, 41, 0xA81A664BBC423001),
633 roundParam512(6, 7, 0, 1, 2, 3, 4, 5, 42, 0xC24B8B70D0F89791),
634 roundParam512(5, 6, 7, 0, 1, 2, 3, 4, 43, 0xC76C51A30654BE30),
635 roundParam512(4, 5, 6, 7, 0, 1, 2, 3, 44, 0xD192E819D6EF5218),
636 roundParam512(3, 4, 5, 6, 7, 0, 1, 2, 45, 0xD69906245565A910),
637 roundParam512(2, 3, 4, 5, 6, 7, 0, 1, 46, 0xF40E35855771202A),
638 roundParam512(1, 2, 3, 4, 5, 6, 7, 0, 47, 0x106AA07032BBD1B8),
639 roundParam512(0, 1, 2, 3, 4, 5, 6, 7, 48, 0x19A4C116B8D2D0C8),
640 roundParam512(7, 0, 1, 2, 3, 4, 5, 6, 49, 0x1E376C085141AB53),
641 roundParam512(6, 7, 0, 1, 2, 3, 4, 5, 50, 0x2748774CDF8EEB99),
642 roundParam512(5, 6, 7, 0, 1, 2, 3, 4, 51, 0x34B0BCB5E19B48A8),
643 roundParam512(4, 5, 6, 7, 0, 1, 2, 3, 52, 0x391C0CB3C5C95A63),
644 roundParam512(3, 4, 5, 6, 7, 0, 1, 2, 53, 0x4ED8AA4AE3418ACB),
645 roundParam512(2, 3, 4, 5, 6, 7, 0, 1, 54, 0x5B9CCA4F7763E373),
646 roundParam512(1, 2, 3, 4, 5, 6, 7, 0, 55, 0x682E6FF3D6B2B8A3),
647 roundParam512(0, 1, 2, 3, 4, 5, 6, 7, 56, 0x748F82EE5DEFB2FC),
648 roundParam512(7, 0, 1, 2, 3, 4, 5, 6, 57, 0x78A5636F43172F60),
649 roundParam512(6, 7, 0, 1, 2, 3, 4, 5, 58, 0x84C87814A1F0AB72),
650 roundParam512(5, 6, 7, 0, 1, 2, 3, 4, 59, 0x8CC702081A6439EC),
651 roundParam512(4, 5, 6, 7, 0, 1, 2, 3, 60, 0x90BEFFFA23631E28),
652 roundParam512(3, 4, 5, 6, 7, 0, 1, 2, 61, 0xA4506CEBDE82BDE9),
653 roundParam512(2, 3, 4, 5, 6, 7, 0, 1, 62, 0xBEF9A3F7B2C67915),
654 roundParam512(1, 2, 3, 4, 5, 6, 7, 0, 63, 0xC67178F2E372532B),
655 roundParam512(0, 1, 2, 3, 4, 5, 6, 7, 64, 0xCA273ECEEA26619C),
656 roundParam512(7, 0, 1, 2, 3, 4, 5, 6, 65, 0xD186B8C721C0C207),
657 roundParam512(6, 7, 0, 1, 2, 3, 4, 5, 66, 0xEADA7DD6CDE0EB1E),
658 roundParam512(5, 6, 7, 0, 1, 2, 3, 4, 67, 0xF57D4F7FEE6ED178),
659 roundParam512(4, 5, 6, 7, 0, 1, 2, 3, 68, 0x06F067AA72176FBA),
660 roundParam512(3, 4, 5, 6, 7, 0, 1, 2, 69, 0x0A637DC5A2C898A6),
661 roundParam512(2, 3, 4, 5, 6, 7, 0, 1, 70, 0x113F9804BEF90DAE),
662 roundParam512(1, 2, 3, 4, 5, 6, 7, 0, 71, 0x1B710B35131C471B),
663 roundParam512(0, 1, 2, 3, 4, 5, 6, 7, 72, 0x28DB77F523047D84),
664 roundParam512(7, 0, 1, 2, 3, 4, 5, 6, 73, 0x32CAAB7B40C72493),
665 roundParam512(6, 7, 0, 1, 2, 3, 4, 5, 74, 0x3C9EBE0A15C9BEBC),
666 roundParam512(5, 6, 7, 0, 1, 2, 3, 4, 75, 0x431D67C49C100D4C),
667 roundParam512(4, 5, 6, 7, 0, 1, 2, 3, 76, 0x4CC5D4BECB3E42B6),
668 roundParam512(3, 4, 5, 6, 7, 0, 1, 2, 77, 0x597F299CFC657E2A),
669 roundParam512(2, 3, 4, 5, 6, 7, 0, 1, 78, 0x5FCB6FAB3AD6FAEC),
670 roundParam512(1, 2, 3, 4, 5, 6, 7, 0, 79, 0x6C44198C4A475817),
671 };
672 inline for (round0) |r| {
673 v[r.h] = v[r.h] +% (math.rotr(u64, v[r.e], @as(u64, 14)) ^ math.rotr(u64, v[r.e], @as(u64, 18)) ^ math.rotr(u64, v[r.e], @as(u64, 41))) +% (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +% r.k +% s[r.i];
674
675 v[r.d] = v[r.d] +% v[r.h];
676
677 v[r.h] = v[r.h] +% (math.rotr(u64, v[r.a], @as(u64, 28)) ^ math.rotr(u64, v[r.a], @as(u64, 34)) ^ math.rotr(u64, v[r.a], @as(u64, 39))) +% ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c]));
678 }
679
680 for (&d.s, v) |*dv, vv| dv.* +%= vv;
681 }
682 };
683}
684
685const RoundParam512 = struct {
686 a: usize,
687 b: usize,
688 c: usize,
689 d: usize,
690 e: usize,
691 f: usize,
692 g: usize,
693 h: usize,
694 i: usize,
695 k: u64,
696};
697
698fn roundParam512(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize, k: u64) RoundParam512 {
699 return RoundParam512{
700 .a = a,
701 .b = b,
702 .c = c,
703 .d = d,
704 .e = e,
705 .f = f,
706 .g = g,
707 .h = h,
708 .i = i,
709 .k = k,
710 };
711}
712
713/// Compute the IV for a truncated version of SHA512 per FIPS 180 Section 5.3.6
714fn truncatedSha512Iv(digest_len: comptime_int) Iv64 {
715 const assert = std.debug.assert;
716 comptime assert(digest_len > 1);
717 comptime assert(digest_len <= 512);
718 comptime assert(digest_len != 384); // NIST specially defines this (see `iv384`)
719
720 comptime var gen_params = iv512;
721 inline for (&gen_params) |*iv| {
722 iv.* ^= 0xa5a5a5a5a5a5a5a5;
723 }
724 const GenHash = Sha2x64(gen_params, 512);
725
726 var params: [@sizeOf(Iv64)]u8 = undefined;
727 const algo_str = std.fmt.comptimePrint("SHA-512/{d}", .{digest_len});
728 GenHash.hash(algo_str, ¶ms, .{});
729
730 return Iv64{
731 std.mem.readInt(u64, params[0..8], .big),
732 std.mem.readInt(u64, params[8..16], .big),
733 std.mem.readInt(u64, params[16..24], .big),
734 std.mem.readInt(u64, params[24..32], .big),
735 std.mem.readInt(u64, params[32..40], .big),
736 std.mem.readInt(u64, params[40..48], .big),
737 std.mem.readInt(u64, params[48..56], .big),
738 std.mem.readInt(u64, params[56..64], .big),
739 };
740}
741
742test truncatedSha512Iv {
743 // Section 5.3.6.1
744 try std.testing.expectEqual(Iv64{
745 0x8C3D37C819544DA2,
746 0x73E1996689DCD4D6,
747 0x1DFAB7AE32FF9C82,
748 0x679DD514582F9FCF,
749 0x0F6D2B697BD44DA8,
750 0x77E36F7304C48942,
751 0x3F9D85A86A1D36C8,
752 0x1112E6AD91D692A1,
753 }, truncatedSha512Iv(224));
754 // Section 5.3.6.2
755 try std.testing.expectEqual(Iv64{
756 0x22312194FC2BF72C,
757 0x9F555FA3C84C64C2,
758 0x2393B86B6F53B151,
759 0x963877195940EABD,
760 0x96283EE2A88EFFE3,
761 0xBE5E1E2553863992,
762 0x2B0199FC2C85B8AA,
763 0x0EB72DDC81C52CA2,
764 }, truncatedSha512Iv(256));
765}
766
767test Sha384 {
768 const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b";
769 try htest.assertEqualHash(Sha384, h1, "");
770
771 const h2 = "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7";
772 try htest.assertEqualHash(Sha384, h2, "abc");
773
774 const h3 = "09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039";
775 try htest.assertEqualHash(Sha384, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
776}
777
778test "sha384 streaming" {
779 var h = Sha384.init(.{});
780 var out: [48]u8 = undefined;
781
782 const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b";
783 h.final(out[0..]);
784 try htest.assertEqual(h1, out[0..]);
785
786 const h2 = "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7";
787
788 h = Sha384.init(.{});
789 h.update("abc");
790 h.final(out[0..]);
791 try htest.assertEqual(h2, out[0..]);
792
793 h = Sha384.init(.{});
794 h.update("a");
795 h.update("b");
796 h.update("c");
797 h.final(out[0..]);
798 try htest.assertEqual(h2, out[0..]);
799}
800
801test Sha512 {
802 const h1 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e";
803 try htest.assertEqualHash(Sha512, h1, "");
804
805 const h2 = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f";
806 try htest.assertEqualHash(Sha512, h2, "abc");
807
808 const h3 = "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909";
809 try htest.assertEqualHash(Sha512, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
810}
811
812test "sha512 streaming" {
813 var h = Sha512.init(.{});
814 var out: [64]u8 = undefined;
815
816 const h1 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e";
817 h.final(out[0..]);
818 try htest.assertEqual(h1, out[0..]);
819
820 const h2 = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f";
821
822 h = Sha512.init(.{});
823 h.update("abc");
824 h.final(out[0..]);
825 try htest.assertEqual(h2, out[0..]);
826
827 h = Sha512.init(.{});
828 h.update("a");
829 h.update("b");
830 h.update("c");
831 h.final(out[0..]);
832 try htest.assertEqual(h2, out[0..]);
833}
834
835test "sha512 aligned final" {
836 var block = [_]u8{0} ** Sha512.block_length;
837 var out: [Sha512.digest_length]u8 = undefined;
838
839 var h = Sha512.init(.{});
840 h.update(&block);
841 h.final(out[0..]);
842}
843
844test Sha512_224 {
845 const h1 = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4";
846 try htest.assertEqualHash(Sha512_224, h1, "");
847
848 const h2 = "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa";
849 try htest.assertEqualHash(Sha512_224, h2, "abc");
850
851 const h3 = "23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9";
852 try htest.assertEqualHash(Sha512_224, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
853}
854
855test Sha512_256 {
856 const h1 = "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a";
857 try htest.assertEqualHash(Sha512_256, h1, "");
858
859 const h2 = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23";
860 try htest.assertEqualHash(Sha512_256, h2, "abc");
861
862 const h3 = "3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a";
863 try htest.assertEqualHash(Sha512_256, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
864}