From 68e279cae33a1b6fa6452d699eb3e39a073518e9 Mon Sep 17 00:00:00 2001 From: clickingbuttons Date: Mon, 22 Apr 2024 17:56:25 -0400 Subject: [PATCH 1/8] std.crypto.hash.sha2: generalize sha512 truncation Replace `Sha512224`, `Sha512256`, and `Sha512T224` with `fn Sha512Truncated(digest_bits: comptime_int)`. This required refactoring `Sha2x64(comptime params)` to `Sha2x64(comptime iv: [8]u64, digest_bits: comptime_int)` for user-specified `digest_bits`. I left #19697 alone but added a compile-time check that digest_bits is divisible by 8. Remove docs which restate type name. Add module docs and reference where IVs come from. --- lib/std/crypto/sha2.zig | 470 +++++++++++++++++----------------------- 1 file changed, 204 insertions(+), 266 deletions(-) diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig index 8debe2cf4041..26029f179537 100644 --- a/lib/std/crypto/sha2.zig +++ b/lib/std/crypto/sha2.zig @@ -1,87 +1,86 @@ +//! Secure Hashing Algorithm 2 (SHA2) +//! +//! A suite of 5 secure hash functions with varying digest lengths: +//! * Sha224 +//! * Sha256 +//! * Sha384 +//! * Sha512 +//! * Sha512/digest_len, where 1 < `digest_len` < 512 and `digest_len` != 384 +//! +//! Published by the National Institue of Standards and Technology (NIST): +//! https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf + const std = @import("../std.zig"); const builtin = @import("builtin"); const mem = std.mem; const math = std.math; const htest = @import("test.zig"); -///////////////////// -// Sha224 + Sha256 - -const RoundParam256 = struct { - a: usize, - b: usize, - c: usize, - d: usize, - e: usize, - f: usize, - g: usize, - h: usize, - i: usize, -}; - -fn roundParam256(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize) RoundParam256 { - return RoundParam256{ - .a = a, - .b = b, - .c = c, - .d = d, - .e = e, - .f = f, - .g = g, - .h = h, - .i = i, - }; +pub const Sha224 = Sha2x32(iv224, 224); +pub const Sha256 = Sha2x32(iv256, 256); +pub const Sha384 = Sha2x64(iv384, 384); +pub const Sha512 = Sha2x64(iv512, 512); +/// This may be more performant than SHA224 and SHA256. +pub fn Sha512Truncated(digest_bits: comptime_int) type { + const iv = sha512iv(digest_bits); + return Sha2x64(iv, digest_bits); } -const Sha2Params32 = struct { - iv0: u32, - iv1: u32, - iv2: u32, - iv3: u32, - iv4: u32, - iv5: u32, - iv6: u32, - iv7: u32, - digest_bits: usize, +/// Low 32 bits of iv384. +const iv224 = IV32{ + 0xC1059ED8, + 0x367CD507, + 0x3070DD17, + 0xF70E5939, + 0xFFC00B31, + 0x68581511, + 0x64F98FA7, + 0xBEFA4FA4, }; - -const Sha224Params = Sha2Params32{ - .iv0 = 0xC1059ED8, - .iv1 = 0x367CD507, - .iv2 = 0x3070DD17, - .iv3 = 0xF70E5939, - .iv4 = 0xFFC00B31, - .iv5 = 0x68581511, - .iv6 = 0x64F98FA7, - .iv7 = 0xBEFA4FA4, - .digest_bits = 224, +/// First thirty-two bits of the fractional parts of the square +/// roots of the first eight prime numbers. +const iv256 = IV32{ + 0x6A09E667, + 0xBB67AE85, + 0x3C6EF372, + 0xA54FF53A, + 0x510E527F, + 0x9B05688C, + 0x1F83D9AB, + 0x5BE0CD19, }; -const Sha256Params = Sha2Params32{ - .iv0 = 0x6A09E667, - .iv1 = 0xBB67AE85, - .iv2 = 0x3C6EF372, - .iv3 = 0xA54FF53A, - .iv4 = 0x510E527F, - .iv5 = 0x9B05688C, - .iv6 = 0x1F83D9AB, - .iv7 = 0x5BE0CD19, - .digest_bits = 256, +/// First sixty-four bits of the fractional parts of the square +/// roots of the ninth through sixteenth prime numbers. +const iv384 = IV64{ + 0xCBBB9D5DC1059ED8, + 0x629A292A367CD507, + 0x9159015A3070DD17, + 0x152FECD8F70E5939, + 0x67332667FFC00B31, + 0x8EB44A8768581511, + 0xDB0C2E0D64F98FA7, + 0x47B5481DBEFA4FA4, +}; +/// First sixty-four bits of the fractional parts of the square +/// roots of the first eight prime numbers. +const iv512 = IV64{ + 0x6A09E667F3BCC908, + 0xBB67AE8584CAA73B, + 0x3C6EF372FE94F82B, + 0xA54FF53A5F1D36F1, + 0x510E527FADE682D1, + 0x9B05688C2B3E6C1F, + 0x1F83D9ABFB41BD6B, + 0x5BE0CD19137E2179, }; -const v4u32 = @Vector(4, u32); - -/// SHA-224 -pub const Sha224 = Sha2x32(Sha224Params); - -/// SHA-256 -pub const Sha256 = Sha2x32(Sha256Params); - -fn Sha2x32(comptime params: Sha2Params32) type { +const IV32 = [8]u32; +fn Sha2x32(comptime iv: IV32, digest_bits: comptime_int) type { return struct { const Self = @This(); pub const block_length = 64; - pub const digest_length = params.digest_bits / 8; + pub const digest_length = digest_bits / 8; pub const Options = struct {}; s: [8]u32 align(16), @@ -92,18 +91,7 @@ fn Sha2x32(comptime params: Sha2Params32) type { pub fn init(options: Options) Self { _ = options; - return Self{ - .s = [_]u32{ - params.iv0, - params.iv1, - params.iv2, - params.iv3, - params.iv4, - params.iv5, - params.iv6, - params.iv7, - }, - }; + return Self{ .s = iv }; } pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void { @@ -168,7 +156,7 @@ fn Sha2x32(comptime params: Sha2Params32) type { d.round(&d.buf); // May truncate for possible 224 output - const rr = d.s[0 .. params.digest_bits / 32]; + const rr = d.s[0 .. digest_length / 4]; for (rr, 0..) |s, j| { mem.writeInt(u32, out[4 * j ..][0..4], s, .big); @@ -199,6 +187,7 @@ fn Sha2x32(comptime params: Sha2Params32) type { } if (!@inComptime()) { + const v4u32 = @Vector(4, u32); switch (builtin.cpu.arch) { .aarch64 => if (builtin.zig_backend != .stage2_c and comptime std.Target.aarch64.featureSetHas(builtin.cpu.features, .sha2)) { var x: v4u32 = d.s[0..4].*; @@ -296,16 +285,7 @@ fn Sha2x32(comptime params: Sha2Params32) type { 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)); } - var v: [8]u32 = [_]u32{ - d.s[0], - d.s[1], - d.s[2], - d.s[3], - d.s[4], - d.s[5], - d.s[6], - d.s[7], - }; + var v: [8]u32 = d.s; const round0 = comptime [_]RoundParam256{ roundParam256(0, 1, 2, 3, 4, 5, 6, 7, 0), @@ -381,14 +361,7 @@ fn Sha2x32(comptime params: Sha2Params32) type { 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])); } - 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]; - d.s[5] +%= v[5]; - d.s[6] +%= v[6]; - d.s[7] +%= v[7]; + for (&d.s, v) |*dv, vv| dv.* +%= vv; } pub const Error = error{}; @@ -405,7 +378,33 @@ fn Sha2x32(comptime params: Sha2Params32) type { }; } -test "sha224 single" { +const RoundParam256 = struct { + a: usize, + b: usize, + c: usize, + d: usize, + e: usize, + f: usize, + g: usize, + h: usize, + i: usize, +}; + +fn roundParam256(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize) RoundParam256 { + return RoundParam256{ + .a = a, + .b = b, + .c = c, + .d = d, + .e = e, + .f = f, + .g = g, + .h = h, + .i = i, + }; +} + +test Sha224 { try htest.assertEqualHash(Sha224, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""); try htest.assertEqualHash(Sha224, "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc"); try htest.assertEqualHash(Sha224, "c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); @@ -466,132 +465,15 @@ test "sha256 aligned final" { h.final(out[0..]); } -///////////////////// -// Sha384 + Sha512 - -const RoundParam512 = struct { - a: usize, - b: usize, - c: usize, - d: usize, - e: usize, - f: usize, - g: usize, - h: usize, - i: usize, - k: u64, -}; - -fn roundParam512(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize, k: u64) RoundParam512 { - return RoundParam512{ - .a = a, - .b = b, - .c = c, - .d = d, - .e = e, - .f = f, - .g = g, - .h = h, - .i = i, - .k = k, - }; -} - -const Sha2Params64 = struct { - iv0: u64, - iv1: u64, - iv2: u64, - iv3: u64, - iv4: u64, - iv5: u64, - iv6: u64, - iv7: u64, - digest_bits: usize, -}; - -const Sha384Params = Sha2Params64{ - .iv0 = 0xCBBB9D5DC1059ED8, - .iv1 = 0x629A292A367CD507, - .iv2 = 0x9159015A3070DD17, - .iv3 = 0x152FECD8F70E5939, - .iv4 = 0x67332667FFC00B31, - .iv5 = 0x8EB44A8768581511, - .iv6 = 0xDB0C2E0D64F98FA7, - .iv7 = 0x47B5481DBEFA4FA4, - .digest_bits = 384, -}; - -const Sha512Params = Sha2Params64{ - .iv0 = 0x6A09E667F3BCC908, - .iv1 = 0xBB67AE8584CAA73B, - .iv2 = 0x3C6EF372FE94F82B, - .iv3 = 0xA54FF53A5F1D36F1, - .iv4 = 0x510E527FADE682D1, - .iv5 = 0x9B05688C2B3E6C1F, - .iv6 = 0x1F83D9ABFB41BD6B, - .iv7 = 0x5BE0CD19137E2179, - .digest_bits = 512, -}; - -const Sha512224Params = Sha2Params64{ - .iv0 = 0x8C3D37C819544DA2, - .iv1 = 0x73E1996689DCD4D6, - .iv2 = 0x1DFAB7AE32FF9C82, - .iv3 = 0x679DD514582F9FCF, - .iv4 = 0x0F6D2B697BD44DA8, - .iv5 = 0x77E36F7304C48942, - .iv6 = 0x3F9D85A86A1D36C8, - .iv7 = 0x1112E6AD91D692A1, - .digest_bits = 224, -}; - -const Sha512256Params = Sha2Params64{ - .iv0 = 0x22312194FC2BF72C, - .iv1 = 0x9F555FA3C84C64C2, - .iv2 = 0x2393B86B6F53B151, - .iv3 = 0x963877195940EABD, - .iv4 = 0x96283EE2A88EFFE3, - .iv5 = 0xBE5E1E2553863992, - .iv6 = 0x2B0199FC2C85B8AA, - .iv7 = 0x0EB72DDC81C52CA2, - .digest_bits = 256, -}; - -const Sha512T256Params = Sha2Params64{ - .iv0 = 0x6A09E667F3BCC908, - .iv1 = 0xBB67AE8584CAA73B, - .iv2 = 0x3C6EF372FE94F82B, - .iv3 = 0xA54FF53A5F1D36F1, - .iv4 = 0x510E527FADE682D1, - .iv5 = 0x9B05688C2B3E6C1F, - .iv6 = 0x1F83D9ABFB41BD6B, - .iv7 = 0x5BE0CD19137E2179, - .digest_bits = 256, -}; - -/// SHA-384 -pub const Sha384 = Sha2x64(Sha384Params); - -/// SHA-512 -pub const Sha512 = Sha2x64(Sha512Params); - -/// SHA-512/224 -pub const Sha512224 = Sha2x64(Sha512224Params); - -/// SHA-512/256 -pub const Sha512256 = Sha2x64(Sha512256Params); - -/// Truncated SHA-512 -pub const Sha512T256 = Sha2x64(Sha512T256Params); - -fn Sha2x64(comptime params: Sha2Params64) type { +const IV64 = [8]u64; +fn Sha2x64(comptime iv: IV64, digest_bits: comptime_int) type { return struct { const Self = @This(); pub const block_length = 128; - pub const digest_length = params.digest_bits / 8; + pub const digest_length = std.math.divCeil(comptime_int, digest_bits, 8) catch unreachable; pub const Options = struct {}; - s: [8]u64, + s: IV64, // Streaming Cache buf: [128]u8 = undefined, buf_len: u8 = 0, @@ -599,18 +481,7 @@ fn Sha2x64(comptime params: Sha2Params64) type { pub fn init(options: Options) Self { _ = options; - return Self{ - .s = [_]u64{ - params.iv0, - params.iv1, - params.iv2, - params.iv3, - params.iv4, - params.iv5, - params.iv6, - params.iv7, - }, - }; + return Self{ .s = iv }; } pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void { @@ -675,18 +546,19 @@ fn Sha2x64(comptime params: Sha2Params64) type { d.round(d.buf[0..]); // May truncate for possible 384 output - const rr = d.s[0 .. params.digest_bits / 64]; + const rr = d.s[0 .. digest_length / 8]; for (rr, 0..) |s, j| { mem.writeInt(u64, out[8 * j ..][0..8], s, .big); } - const bytes_left = params.digest_bits / 8 % 8; + if (digest_bits % 8 != 0) @compileError("impl doesn't support non-byte digest_len"); + const bytes_left = digest_bits / 8 % 8; if (bytes_left > 0) { - const rest = d.s[(params.digest_bits / 64)]; + const rest = d.s[(digest_bits / 64)]; var buf: [8]u8 = undefined; std.mem.writeInt(u64, &buf, rest, .big); - @memcpy(out[params.digest_bits / 64 * 8 ..], buf[0..bytes_left]); + @memcpy(out[digest_bits / 64 * 8 ..], buf[0..bytes_left]); } } @@ -709,16 +581,7 @@ fn Sha2x64(comptime params: Sha2Params64) type { (math.rotr(u64, s[i - 2], @as(u64, 19)) ^ math.rotr(u64, s[i - 2], @as(u64, 61)) ^ (s[i - 2] >> 6)); } - var v: [8]u64 = [_]u64{ - d.s[0], - d.s[1], - d.s[2], - d.s[3], - d.s[4], - d.s[5], - d.s[6], - d.s[7], - }; + var v: [8]u64 = d.s; const round0 = comptime [_]RoundParam512{ roundParam512(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98D728AE22), @@ -810,19 +673,92 @@ fn Sha2x64(comptime params: Sha2Params64) type { 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])); } - 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]; - d.s[5] +%= v[5]; - d.s[6] +%= v[6]; - d.s[7] +%= v[7]; + for (&d.s, v) |*dv, vv| dv.* +%= vv; } }; } -test "sha384 single" { +const RoundParam512 = struct { + a: usize, + b: usize, + c: usize, + d: usize, + e: usize, + f: usize, + g: usize, + h: usize, + i: usize, + k: u64, +}; + +fn roundParam512(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize, k: u64) RoundParam512 { + return RoundParam512{ + .a = a, + .b = b, + .c = c, + .d = d, + .e = e, + .f = f, + .g = g, + .h = h, + .i = i, + .k = k, + }; +} + +fn sha512iv(digest_len: comptime_int) IV64 { + if (digest_len < 1 or digest_len >= 512 or digest_len == 384) { + @compileError("digest_len must be between 1 and 512 but not 384"); + } + + comptime var gen_params = iv512; + inline for (&gen_params) |*iv| { + iv.* ^= 0xa5a5a5a5a5a5a5a5; + } + const GenHash = Sha2x64(gen_params, 512); + + var params: [@sizeOf(IV64)]u8 = undefined; + const algo_str = std.fmt.comptimePrint("SHA-512/{d}", .{digest_len}); + GenHash.hash(algo_str, ¶ms, .{}); + + return IV64{ + std.mem.readInt(u64, params[0..8], .big), + std.mem.readInt(u64, params[8..16], .big), + std.mem.readInt(u64, params[16..24], .big), + std.mem.readInt(u64, params[24..32], .big), + std.mem.readInt(u64, params[32..40], .big), + std.mem.readInt(u64, params[40..48], .big), + std.mem.readInt(u64, params[48..56], .big), + std.mem.readInt(u64, params[56..64], .big), + }; +} + +test sha512iv { + // Section 5.3.6.1 + try std.testing.expectEqual(IV64{ + 0x8C3D37C819544DA2, + 0x73E1996689DCD4D6, + 0x1DFAB7AE32FF9C82, + 0x679DD514582F9FCF, + 0x0F6D2B697BD44DA8, + 0x77E36F7304C48942, + 0x3F9D85A86A1D36C8, + 0x1112E6AD91D692A1, + }, sha512iv(224)); + // Section 5.3.6.2 + try std.testing.expectEqual(IV64{ + 0x22312194FC2BF72C, + 0x9F555FA3C84C64C2, + 0x2393B86B6F53B151, + 0x963877195940EABD, + 0x96283EE2A88EFFE3, + 0xBE5E1E2553863992, + 0x2B0199FC2C85B8AA, + 0x0EB72DDC81C52CA2, + }, sha512iv(256)); +} + +test Sha384 { const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"; try htest.assertEqualHash(Sha384, h1, ""); @@ -856,7 +792,7 @@ test "sha384 streaming" { try htest.assertEqual(h2, out[0..]); } -test "sha512 single" { +test Sha512 { const h1 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"; try htest.assertEqualHash(Sha512, h1, ""); @@ -899,24 +835,26 @@ test "sha512 aligned final" { h.final(out[0..]); } -test "sha512-224 single" { - const h1 = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"; - try htest.assertEqualHash(Sha512224, h1, ""); +test "Sha512Truncated(224)" { + const Sha512_224 = Sha512Truncated(224); + // const h1 = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"; + // try htest.assertEqualHash(Sha512_224, h1, ""); const h2 = "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"; - try htest.assertEqualHash(Sha512224, h2, "abc"); + try htest.assertEqualHash(Sha512_224, h2, "abc"); const h3 = "23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9"; - try htest.assertEqualHash(Sha512224, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); + try htest.assertEqualHash(Sha512_224, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); } -test "sha512-256 single" { +test "Sha512Truncated(256)" { + const Sha512_256 = Sha512Truncated(256); const h1 = "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"; - try htest.assertEqualHash(Sha512256, h1, ""); + try htest.assertEqualHash(Sha512_256, h1, ""); const h2 = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"; - try htest.assertEqualHash(Sha512256, h2, "abc"); + try htest.assertEqualHash(Sha512_256, h2, "abc"); const h3 = "3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a"; - try htest.assertEqualHash(Sha512256, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); + try htest.assertEqualHash(Sha512_256, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); } From 893b9163f5c9ff6d327bc560dfe41caeacea908d Mon Sep 17 00:00:00 2001 From: clickingbuttons Date: Mon, 22 Apr 2024 19:27:44 -0400 Subject: [PATCH 2/8] std.crypto.sha2: make Sha512_224 and Sha512_256 pub --- lib/std/crypto/sha2.zig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig index 26029f179537..cd9ef7b91c75 100644 --- a/lib/std/crypto/sha2.zig +++ b/lib/std/crypto/sha2.zig @@ -20,6 +20,8 @@ pub const Sha224 = Sha2x32(iv224, 224); pub const Sha256 = Sha2x32(iv256, 256); pub const Sha384 = Sha2x64(iv384, 384); pub const Sha512 = Sha2x64(iv512, 512); +pub const Sha512_224 = Sha512Truncated(224); +pub const Sha512_256 = Sha512Truncated(256); /// This may be more performant than SHA224 and SHA256. pub fn Sha512Truncated(digest_bits: comptime_int) type { const iv = sha512iv(digest_bits); @@ -835,10 +837,9 @@ test "sha512 aligned final" { h.final(out[0..]); } -test "Sha512Truncated(224)" { - const Sha512_224 = Sha512Truncated(224); - // const h1 = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"; - // try htest.assertEqualHash(Sha512_224, h1, ""); +test Sha512_224 { + const h1 = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"; + try htest.assertEqualHash(Sha512_224, h1, ""); const h2 = "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"; try htest.assertEqualHash(Sha512_224, h2, "abc"); @@ -847,8 +848,7 @@ test "Sha512Truncated(224)" { try htest.assertEqualHash(Sha512_224, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); } -test "Sha512Truncated(256)" { - const Sha512_256 = Sha512Truncated(256); +test Sha512_256 { const h1 = "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"; try htest.assertEqualHash(Sha512_256, h1, ""); From 040aa5ca1bdb70d0da0e1c9db2f3639c2dda08b8 Mon Sep 17 00:00:00 2001 From: clickingbuttons Date: Tue, 23 Apr 2024 13:11:47 -0400 Subject: [PATCH 3/8] make generic type implementation detail, add comments --- lib/std/crypto/sha2.zig | 63 +++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig index cd9ef7b91c75..a8b1a4b479cd 100644 --- a/lib/std/crypto/sha2.zig +++ b/lib/std/crypto/sha2.zig @@ -9,6 +9,7 @@ //! //! Published by the National Institue of Standards and Technology (NIST): //! https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf +//! https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-208.pdf const std = @import("../std.zig"); const builtin = @import("builtin"); @@ -20,16 +21,41 @@ pub const Sha224 = Sha2x32(iv224, 224); pub const Sha256 = Sha2x32(iv256, 256); pub const Sha384 = Sha2x64(iv384, 384); pub const Sha512 = Sha2x64(iv512, 512); -pub const Sha512_224 = Sha512Truncated(224); -pub const Sha512_256 = Sha512Truncated(256); + +// Truncated variants that save space but reduce security. +// T = original IV +// _ = changed IV +/// SHA-256 truncated to 192 bits per FIPS 800-208. +pub const Sha256T192 = Sha2x32(iv256, 256); + +/// SHA-512 truncated to 224 bits per FIPS 180. +pub const Sha512_224 = Sha512Truncated(224, IvStrategy.change); +/// SHA-512 truncated to 224 bits. +pub const Sha512T224 = Sha512Truncated(224, IvStrategy.keep); + +/// SHA-512 truncated to 256 bits per FIPS 180. +pub const Sha512_256 = Sha512Truncated(256, IvStrategy.change); +/// SHA-512 truncated to 256 bits. +pub const Sha512T256 = Sha512Truncated(256, IvStrategy.keep); + +const IvStrategy = enum { + /// Change the IV per NIST FIPS 180 (released 2012). + change, + /// Use the original IV (released 2001). + keep, +}; +/// SHA-512 truncated to length `digest_bits` using `iv_strategy`. /// This may be more performant than SHA224 and SHA256. -pub fn Sha512Truncated(digest_bits: comptime_int) type { - const iv = sha512iv(digest_bits); +fn Sha512Truncated(digest_bits: comptime_int, comptime iv_strategy: IvStrategy) type { + const iv = switch (iv_strategy) { + .change => sha512iv(digest_bits), + .keep => iv256, + }; return Sha2x64(iv, digest_bits); } /// Low 32 bits of iv384. -const iv224 = IV32{ +const iv224 = Iv32{ 0xC1059ED8, 0x367CD507, 0x3070DD17, @@ -41,7 +67,7 @@ const iv224 = IV32{ }; /// First thirty-two bits of the fractional parts of the square /// roots of the first eight prime numbers. -const iv256 = IV32{ +const iv256 = Iv32{ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, @@ -54,7 +80,7 @@ const iv256 = IV32{ /// First sixty-four bits of the fractional parts of the square /// roots of the ninth through sixteenth prime numbers. -const iv384 = IV64{ +const iv384 = Iv64{ 0xCBBB9D5DC1059ED8, 0x629A292A367CD507, 0x9159015A3070DD17, @@ -66,7 +92,7 @@ const iv384 = IV64{ }; /// First sixty-four bits of the fractional parts of the square /// roots of the first eight prime numbers. -const iv512 = IV64{ +const iv512 = Iv64{ 0x6A09E667F3BCC908, 0xBB67AE8584CAA73B, 0x3C6EF372FE94F82B, @@ -77,8 +103,8 @@ const iv512 = IV64{ 0x5BE0CD19137E2179, }; -const IV32 = [8]u32; -fn Sha2x32(comptime iv: IV32, digest_bits: comptime_int) type { +const Iv32 = [8]u32; +fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type { return struct { const Self = @This(); pub const block_length = 64; @@ -467,15 +493,15 @@ test "sha256 aligned final" { h.final(out[0..]); } -const IV64 = [8]u64; -fn Sha2x64(comptime iv: IV64, digest_bits: comptime_int) type { +const Iv64 = [8]u64; +fn Sha2x64(comptime iv: Iv64, digest_bits: comptime_int) type { return struct { const Self = @This(); pub const block_length = 128; pub const digest_length = std.math.divCeil(comptime_int, digest_bits, 8) catch unreachable; pub const Options = struct {}; - s: IV64, + s: Iv64, // Streaming Cache buf: [128]u8 = undefined, buf_len: u8 = 0, @@ -708,7 +734,8 @@ fn roundParam512(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: }; } -fn sha512iv(digest_len: comptime_int) IV64 { +/// FIPS 180 Section 5.3.6 +fn sha512iv(digest_len: comptime_int) Iv64 { if (digest_len < 1 or digest_len >= 512 or digest_len == 384) { @compileError("digest_len must be between 1 and 512 but not 384"); } @@ -719,11 +746,11 @@ fn sha512iv(digest_len: comptime_int) IV64 { } const GenHash = Sha2x64(gen_params, 512); - var params: [@sizeOf(IV64)]u8 = undefined; + var params: [@sizeOf(Iv64)]u8 = undefined; const algo_str = std.fmt.comptimePrint("SHA-512/{d}", .{digest_len}); GenHash.hash(algo_str, ¶ms, .{}); - return IV64{ + return Iv64{ std.mem.readInt(u64, params[0..8], .big), std.mem.readInt(u64, params[8..16], .big), std.mem.readInt(u64, params[16..24], .big), @@ -737,7 +764,7 @@ fn sha512iv(digest_len: comptime_int) IV64 { test sha512iv { // Section 5.3.6.1 - try std.testing.expectEqual(IV64{ + try std.testing.expectEqual(Iv64{ 0x8C3D37C819544DA2, 0x73E1996689DCD4D6, 0x1DFAB7AE32FF9C82, @@ -748,7 +775,7 @@ test sha512iv { 0x1112E6AD91D692A1, }, sha512iv(224)); // Section 5.3.6.2 - try std.testing.expectEqual(IV64{ + try std.testing.expectEqual(Iv64{ 0x22312194FC2BF72C, 0x9F555FA3C84C64C2, 0x2393B86B6F53B151, From d698fa161402a00889f592b757cc1c9c99422d51 Mon Sep 17 00:00:00 2001 From: clickingbuttons Date: Tue, 23 Apr 2024 15:39:24 -0400 Subject: [PATCH 4/8] fix iv --- lib/std/crypto/sha2.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig index a8b1a4b479cd..a52083ace53c 100644 --- a/lib/std/crypto/sha2.zig +++ b/lib/std/crypto/sha2.zig @@ -49,7 +49,7 @@ const IvStrategy = enum { fn Sha512Truncated(digest_bits: comptime_int, comptime iv_strategy: IvStrategy) type { const iv = switch (iv_strategy) { .change => sha512iv(digest_bits), - .keep => iv256, + .keep => iv512, }; return Sha2x64(iv, digest_bits); } From 1fb2a4104d37e59252ff9100cfb48db3640eecb5 Mon Sep 17 00:00:00 2001 From: clickingbuttons Date: Tue, 23 Apr 2024 16:44:13 -0400 Subject: [PATCH 5/8] address @jedisct1 feedback --- lib/std/crypto/sha2.zig | 137 ++++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 76 deletions(-) diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig index a52083ace53c..0ab90fc42233 100644 --- a/lib/std/crypto/sha2.zig +++ b/lib/std/crypto/sha2.zig @@ -22,37 +22,43 @@ pub const Sha256 = Sha2x32(iv256, 256); pub const Sha384 = Sha2x64(iv384, 384); pub const Sha512 = Sha2x64(iv512, 512); -// Truncated variants that save space but reduce security. -// T = original IV -// _ = changed IV -/// SHA-256 truncated to 192 bits per FIPS 800-208. -pub const Sha256T192 = Sha2x32(iv256, 256); - -/// SHA-512 truncated to 224 bits per FIPS 180. -pub const Sha512_224 = Sha512Truncated(224, IvStrategy.change); -/// SHA-512 truncated to 224 bits. -pub const Sha512T224 = Sha512Truncated(224, IvStrategy.keep); - -/// SHA-512 truncated to 256 bits per FIPS 180. -pub const Sha512_256 = Sha512Truncated(256, IvStrategy.change); -/// SHA-512 truncated to 256 bits. -pub const Sha512T256 = Sha512Truncated(256, IvStrategy.keep); - -const IvStrategy = enum { - /// Change the IV per NIST FIPS 180 (released 2012). - change, - /// Use the original IV (released 2001). - keep, +/// Truncating the output of SHA2-based hash functions improves security by mitigating +/// length-extension attacks. Collision attacks remain impractical for all the types defined here. +/// T: original hash function, whose output is simply truncated. +/// A truncated output is just the first bytes of a longer output. +/// _: hash function with context separation. +/// Different lengths produce completely different outputs. +pub const truncated = struct { + pub const Sha256T192 = Sha2x32(iv256, 256); + + pub const Sha512_224 = Sha2x64(sha512Iv(224), 224); + pub const Sha512_256 = Sha2x64(sha512Iv(iv512), 256); + + pub const Sha512T224 = Sha2x64(iv512, 224); + pub const Sha512T256 = Sha2x64(iv512, 256); + + test Sha512_224 { + const h1 = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"; + try htest.assertEqualHash(Sha512_224, h1, ""); + + const h2 = "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"; + try htest.assertEqualHash(Sha512_224, h2, "abc"); + + const h3 = "23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9"; + try htest.assertEqualHash(Sha512_224, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); + } + + test Sha512_256 { + const h1 = "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"; + try htest.assertEqualHash(Sha512_256, h1, ""); + + const h2 = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"; + try htest.assertEqualHash(Sha512_256, h2, "abc"); + + const h3 = "3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a"; + try htest.assertEqualHash(Sha512_256, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); + } }; -/// SHA-512 truncated to length `digest_bits` using `iv_strategy`. -/// This may be more performant than SHA224 and SHA256. -fn Sha512Truncated(digest_bits: comptime_int, comptime iv_strategy: IvStrategy) type { - const iv = switch (iv_strategy) { - .change => sha512iv(digest_bits), - .keep => iv512, - }; - return Sha2x64(iv, digest_bits); -} /// Low 32 bits of iv384. const iv224 = Iv32{ @@ -215,12 +221,12 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type { } if (!@inComptime()) { - const v4u32 = @Vector(4, u32); + const V4u32 = @Vector(4, u32); switch (builtin.cpu.arch) { .aarch64 => if (builtin.zig_backend != .stage2_c and comptime std.Target.aarch64.featureSetHas(builtin.cpu.features, .sha2)) { - var x: v4u32 = d.s[0..4].*; - var y: v4u32 = d.s[4..8].*; - const s_v = @as(*[16]v4u32, @ptrCast(&s)); + var x: V4u32 = d.s[0..4].*; + var y: V4u32 = d.s[4..8].*; + const s_v = @as(*[16]V4u32, @ptrCast(&s)); comptime var k: u8 = 0; inline while (k < 16) : (k += 1) { @@ -228,7 +234,7 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type { s_v[k] = asm ( \\sha256su0.4s %[w0_3], %[w4_7] \\sha256su1.4s %[w0_3], %[w8_11], %[w12_15] - : [w0_3] "=w" (-> v4u32), + : [w0_3] "=w" (-> V4u32), : [_] "0" (s_v[k - 4]), [w4_7] "w" (s_v[k - 3]), [w8_11] "w" (s_v[k - 2]), @@ -236,7 +242,7 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type { ); } - const w: v4u32 = s_v[k] +% @as(v4u32, W[4 * k ..][0..4].*); + const w: V4u32 = s_v[k] +% @as(V4u32, W[4 * k ..][0..4].*); asm volatile ( \\mov.4s v0, %[x] \\sha256h.4s %[x], %[y], %[w] @@ -250,15 +256,15 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type { ); } - d.s[0..4].* = x +% @as(v4u32, d.s[0..4].*); - d.s[4..8].* = y +% @as(v4u32, d.s[4..8].*); + d.s[0..4].* = x +% @as(V4u32, d.s[0..4].*); + d.s[4..8].* = y +% @as(V4u32, d.s[4..8].*); return; }, // C backend doesn't currently support passing vectors to inline asm. .x86_64 => if (builtin.zig_backend != .stage2_c and comptime std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sha, .avx2 })) { - var x: v4u32 = [_]u32{ d.s[5], d.s[4], d.s[1], d.s[0] }; - var y: v4u32 = [_]u32{ d.s[7], d.s[6], d.s[3], d.s[2] }; - const s_v = @as(*[16]v4u32, @ptrCast(&s)); + var x: V4u32 = [_]u32{ d.s[5], d.s[4], d.s[1], d.s[0] }; + var y: V4u32 = [_]u32{ d.s[7], d.s[6], d.s[3], d.s[2] }; + const s_v = @as(*[16]V4u32, @ptrCast(&s)); comptime var k: u8 = 0; inline while (k < 16) : (k += 1) { @@ -270,7 +276,7 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type { \\ paddd %[tmp], %[result] \\ sha256msg2 %[w12_15], %[result] : [tmp] "=&x" (tmp), - [result] "=&x" (-> v4u32), + [result] "=&x" (-> V4u32), : [_] "0" (tmp), [w4_7] "x" (s_v[k + 1]), [w8_11] "x" (s_v[k + 2]), @@ -278,19 +284,19 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type { ); } - const w: v4u32 = s_v[k] +% @as(v4u32, W[4 * k ..][0..4].*); + const w: V4u32 = s_v[k] +% @as(V4u32, W[4 * k ..][0..4].*); y = asm ("sha256rnds2 %[x], %[y]" - : [y] "=x" (-> v4u32), + : [y] "=x" (-> V4u32), : [_] "0" (y), [x] "x" (x), [_] "{xmm0}" (w), ); x = asm ("sha256rnds2 %[y], %[x]" - : [x] "=x" (-> v4u32), + : [x] "=x" (-> V4u32), : [_] "0" (x), [y] "x" (y), - [_] "{xmm0}" (@as(v4u32, @bitCast(@as(u128, @bitCast(w)) >> 64))), + [_] "{xmm0}" (@as(V4u32, @bitCast(@as(u128, @bitCast(w)) >> 64))), ); } @@ -734,11 +740,12 @@ fn roundParam512(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: }; } -/// FIPS 180 Section 5.3.6 -fn sha512iv(digest_len: comptime_int) Iv64 { - if (digest_len < 1 or digest_len >= 512 or digest_len == 384) { - @compileError("digest_len must be between 1 and 512 but not 384"); - } +/// Compute the IV for a truncated version of SHA512 per FIPS 180 Section 5.3.6 +fn sha512Iv(digest_len: comptime_int) Iv64 { + const assert = std.debug.assert; + comptime assert(digest_len > 1); + comptime assert(digest_len <= 512); + comptime assert(digest_len != 384); // NIST specially defines this (see `iv384`) comptime var gen_params = iv512; inline for (&gen_params) |*iv| { @@ -762,7 +769,7 @@ fn sha512iv(digest_len: comptime_int) Iv64 { }; } -test sha512iv { +test sha512Iv { // Section 5.3.6.1 try std.testing.expectEqual(Iv64{ 0x8C3D37C819544DA2, @@ -773,7 +780,7 @@ test sha512iv { 0x77E36F7304C48942, 0x3F9D85A86A1D36C8, 0x1112E6AD91D692A1, - }, sha512iv(224)); + }, sha512Iv(224)); // Section 5.3.6.2 try std.testing.expectEqual(Iv64{ 0x22312194FC2BF72C, @@ -784,7 +791,7 @@ test sha512iv { 0xBE5E1E2553863992, 0x2B0199FC2C85B8AA, 0x0EB72DDC81C52CA2, - }, sha512iv(256)); + }, sha512Iv(256)); } test Sha384 { @@ -863,25 +870,3 @@ test "sha512 aligned final" { h.update(&block); h.final(out[0..]); } - -test Sha512_224 { - const h1 = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"; - try htest.assertEqualHash(Sha512_224, h1, ""); - - const h2 = "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"; - try htest.assertEqualHash(Sha512_224, h2, "abc"); - - const h3 = "23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9"; - try htest.assertEqualHash(Sha512_224, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); -} - -test Sha512_256 { - const h1 = "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"; - try htest.assertEqualHash(Sha512_256, h1, ""); - - const h2 = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"; - try htest.assertEqualHash(Sha512_256, h2, "abc"); - - const h3 = "3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a"; - try htest.assertEqualHash(Sha512_256, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); -} From 35408ff023d2f1148f2909408faea5b4f8ea82d1 Mon Sep 17 00:00:00 2001 From: clickingbuttons Date: Tue, 23 Apr 2024 16:45:58 -0400 Subject: [PATCH 6/8] fix typo --- lib/std/crypto/sha2.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig index 0ab90fc42233..a85f8605036b 100644 --- a/lib/std/crypto/sha2.zig +++ b/lib/std/crypto/sha2.zig @@ -32,7 +32,7 @@ pub const truncated = struct { pub const Sha256T192 = Sha2x32(iv256, 256); pub const Sha512_224 = Sha2x64(sha512Iv(224), 224); - pub const Sha512_256 = Sha2x64(sha512Iv(iv512), 256); + pub const Sha512_256 = Sha2x64(sha512Iv(256), 256); pub const Sha512T224 = Sha2x64(iv512, 224); pub const Sha512T256 = Sha2x64(iv512, 256); From 89985ed4fcef9fe1313687be1561e3712299b23d Mon Sep 17 00:00:00 2001 From: clickingbuttons Date: Tue, 23 Apr 2024 17:28:03 -0400 Subject: [PATCH 7/8] renaming --- lib/std/crypto/sha2.zig | 90 +++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 48 deletions(-) diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig index a85f8605036b..8df329a2925b 100644 --- a/lib/std/crypto/sha2.zig +++ b/lib/std/crypto/sha2.zig @@ -1,15 +1,17 @@ //! Secure Hashing Algorithm 2 (SHA2) //! -//! A suite of 5 secure hash functions with varying digest lengths: -//! * Sha224 -//! * Sha256 -//! * Sha384 -//! * Sha512 -//! * Sha512/digest_len, where 1 < `digest_len` < 512 and `digest_len` != 384 +//! Published by the National Institue of Standards and Technology (NIST) [1] [2]. //! -//! Published by the National Institue of Standards and Technology (NIST): -//! https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf -//! https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-208.pdf +//! Truncation mitigates length-extension attacks but increases vulnerability to collision +//! attacks. Collision attacks remain impractical for all types defined here. +//! +//! T: original hash function, whose output is simply truncated. +//! A truncated output is just the first bytes of a longer output. +//! _: hash function with context separation. +//! Different lengths produce completely different outputs. +//! +//! [1] https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf +//! [2] https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-208.pdf const std = @import("../std.zig"); const builtin = @import("builtin"); @@ -22,43 +24,13 @@ pub const Sha256 = Sha2x32(iv256, 256); pub const Sha384 = Sha2x64(iv384, 384); pub const Sha512 = Sha2x64(iv512, 512); -/// Truncating the output of SHA2-based hash functions improves security by mitigating -/// length-extension attacks. Collision attacks remain impractical for all the types defined here. -/// T: original hash function, whose output is simply truncated. -/// A truncated output is just the first bytes of a longer output. -/// _: hash function with context separation. -/// Different lengths produce completely different outputs. -pub const truncated = struct { - pub const Sha256T192 = Sha2x32(iv256, 256); - - pub const Sha512_224 = Sha2x64(sha512Iv(224), 224); - pub const Sha512_256 = Sha2x64(sha512Iv(256), 256); - - pub const Sha512T224 = Sha2x64(iv512, 224); - pub const Sha512T256 = Sha2x64(iv512, 256); - - test Sha512_224 { - const h1 = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"; - try htest.assertEqualHash(Sha512_224, h1, ""); +pub const Sha256T192 = Sha2x32(iv256, 256); - const h2 = "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"; - try htest.assertEqualHash(Sha512_224, h2, "abc"); +pub const Sha512T224 = Sha2x64(iv512, 224); +pub const Sha512T256 = Sha2x64(iv512, 256); - const h3 = "23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9"; - try htest.assertEqualHash(Sha512_224, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); - } - - test Sha512_256 { - const h1 = "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"; - try htest.assertEqualHash(Sha512_256, h1, ""); - - const h2 = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"; - try htest.assertEqualHash(Sha512_256, h2, "abc"); - - const h3 = "3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a"; - try htest.assertEqualHash(Sha512_256, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); - } -}; +pub const Sha512_224 = Sha2x64(truncatedSha512Iv(224), 224); +pub const Sha512_256 = Sha2x64(truncatedSha512Iv(256), 256); /// Low 32 bits of iv384. const iv224 = Iv32{ @@ -741,7 +713,7 @@ fn roundParam512(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: } /// Compute the IV for a truncated version of SHA512 per FIPS 180 Section 5.3.6 -fn sha512Iv(digest_len: comptime_int) Iv64 { +fn truncatedSha512Iv(digest_len: comptime_int) Iv64 { const assert = std.debug.assert; comptime assert(digest_len > 1); comptime assert(digest_len <= 512); @@ -769,7 +741,7 @@ fn sha512Iv(digest_len: comptime_int) Iv64 { }; } -test sha512Iv { +test truncatedSha512Iv { // Section 5.3.6.1 try std.testing.expectEqual(Iv64{ 0x8C3D37C819544DA2, @@ -780,7 +752,7 @@ test sha512Iv { 0x77E36F7304C48942, 0x3F9D85A86A1D36C8, 0x1112E6AD91D692A1, - }, sha512Iv(224)); + }, truncatedSha512Iv(224)); // Section 5.3.6.2 try std.testing.expectEqual(Iv64{ 0x22312194FC2BF72C, @@ -791,7 +763,7 @@ test sha512Iv { 0xBE5E1E2553863992, 0x2B0199FC2C85B8AA, 0x0EB72DDC81C52CA2, - }, sha512Iv(256)); + }, truncatedSha512Iv(256)); } test Sha384 { @@ -870,3 +842,25 @@ test "sha512 aligned final" { h.update(&block); h.final(out[0..]); } + +test Sha512_224 { + const h1 = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"; + try htest.assertEqualHash(Sha512_224, h1, ""); + + const h2 = "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"; + try htest.assertEqualHash(Sha512_224, h2, "abc"); + + const h3 = "23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9"; + try htest.assertEqualHash(Sha512_224, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); +} + +test Sha512_256 { + const h1 = "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"; + try htest.assertEqualHash(Sha512_256, h1, ""); + + const h2 = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"; + try htest.assertEqualHash(Sha512_256, h2, "abc"); + + const h3 = "3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a"; + try htest.assertEqualHash(Sha512_256, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); +} From fd9f66562d3aab7a600b32bb6f12a182cf1dbee8 Mon Sep 17 00:00:00 2001 From: clickingbuttons Date: Fri, 26 Apr 2024 17:09:06 -0400 Subject: [PATCH 8/8] add truncation clarifying comment and Sha259T192 tests --- lib/std/crypto/sha2.zig | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig index 8df329a2925b..c4556b668b92 100644 --- a/lib/std/crypto/sha2.zig +++ b/lib/std/crypto/sha2.zig @@ -24,12 +24,17 @@ pub const Sha256 = Sha2x32(iv256, 256); pub const Sha384 = Sha2x64(iv384, 384); pub const Sha512 = Sha2x64(iv512, 512); -pub const Sha256T192 = Sha2x32(iv256, 256); +/// SHA-256 truncated to leftmost 192 bits. +pub const Sha256T192 = Sha2x32(iv256, 192); +/// SHA-512 truncated to leftmost 224 bits. pub const Sha512T224 = Sha2x64(iv512, 224); +/// SHA-512 truncated to leftmost 256 bits. pub const Sha512T256 = Sha2x64(iv512, 256); +/// SHA-512 with a different initialization vector truncated to leftmost 224 bits. pub const Sha512_224 = Sha2x64(truncatedSha512Iv(224), 224); +/// SHA-512 with a different initialization vector truncated to leftmost 256 bits. pub const Sha512_256 = Sha2x64(truncatedSha512Iv(256), 256); /// Low 32 bits of iv384. @@ -161,7 +166,7 @@ fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type { d.round(&d.buf); - // May truncate for possible 224 output + // May truncate for possible 224 or 192 output const rr = d.s[0 .. digest_length / 4]; for (rr, 0..) |s, j| { @@ -436,12 +441,18 @@ test "sha224 streaming" { try htest.assertEqual("23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", out[0..]); } -test "sha256 single" { +test Sha256 { try htest.assertEqualHash(Sha256, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""); try htest.assertEqualHash(Sha256, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc"); try htest.assertEqualHash(Sha256, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); } +test Sha256T192 { + try htest.assertEqualHash(Sha256T192, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934c", ""); + try htest.assertEqualHash(Sha256T192, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9c", "abc"); + try htest.assertEqualHash(Sha256T192, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); +} + test "sha256 streaming" { var h = Sha256.init(.{}); var out: [32]u8 = undefined;