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, &params, .{});
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}