| ... | ... | @@ -1,87 +1,97 @@ |
| 1 | //! Secure Hashing Algorithm 2 (SHA2) |
| 2 | //! |
| 3 | //! Published by the National Institue 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 | |
| 1 | 16 | const std = @import("../std.zig"); |
| 2 | 17 | const builtin = @import("builtin"); |
| 3 | 18 | const mem = std.mem; |
| 4 | 19 | const math = std.math; |
| 5 | 20 | const htest = @import("test.zig"); |
| 6 | 21 | |
| 7 | | ///////////////////// |
| 8 | | // Sha224 + Sha256 |
| 9 | | |
| 10 | | const RoundParam256 = struct { |
| 11 | | a: usize, |
| 12 | | b: usize, |
| 13 | | c: usize, |
| 14 | | d: usize, |
| 15 | | e: usize, |
| 16 | | f: usize, |
| 17 | | g: usize, |
| 18 | | h: usize, |
| 19 | | i: usize, |
| 22 | pub const Sha224 = Sha2x32(iv224, 224); |
| 23 | pub const Sha256 = Sha2x32(iv256, 256); |
| 24 | pub const Sha384 = Sha2x64(iv384, 384); |
| 25 | pub const Sha512 = Sha2x64(iv512, 512); |
| 26 | |
| 27 | /// SHA-256 truncated to leftmost 192 bits. |
| 28 | pub const Sha256T192 = Sha2x32(iv256, 192); |
| 29 | |
| 30 | /// SHA-512 truncated to leftmost 224 bits. |
| 31 | pub const Sha512T224 = Sha2x64(iv512, 224); |
| 32 | /// SHA-512 truncated to leftmost 256 bits. |
| 33 | pub const Sha512T256 = Sha2x64(iv512, 256); |
| 34 | |
| 35 | /// SHA-512 with a different initialization vector truncated to leftmost 224 bits. |
| 36 | pub const Sha512_224 = Sha2x64(truncatedSha512Iv(224), 224); |
| 37 | /// SHA-512 with a different initialization vector truncated to leftmost 256 bits. |
| 38 | pub const Sha512_256 = Sha2x64(truncatedSha512Iv(256), 256); |
| 39 | |
| 40 | /// Low 32 bits of iv384. |
| 41 | const iv224 = Iv32{ |
| 42 | 0xC1059ED8, |
| 43 | 0x367CD507, |
| 44 | 0x3070DD17, |
| 45 | 0xF70E5939, |
| 46 | 0xFFC00B31, |
| 47 | 0x68581511, |
| 48 | 0x64F98FA7, |
| 49 | 0xBEFA4FA4, |
| 20 | 50 | }; |
| 21 | | |
| 22 | | fn roundParam256(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize) RoundParam256 { |
| 23 | | return RoundParam256{ |
| 24 | | .a = a, |
| 25 | | .b = b, |
| 26 | | .c = c, |
| 27 | | .d = d, |
| 28 | | .e = e, |
| 29 | | .f = f, |
| 30 | | .g = g, |
| 31 | | .h = h, |
| 32 | | .i = i, |
| 33 | | }; |
| 34 | | } |
| 35 | | |
| 36 | | const Sha2Params32 = struct { |
| 37 | | iv0: u32, |
| 38 | | iv1: u32, |
| 39 | | iv2: u32, |
| 40 | | iv3: u32, |
| 41 | | iv4: u32, |
| 42 | | iv5: u32, |
| 43 | | iv6: u32, |
| 44 | | iv7: u32, |
| 45 | | digest_bits: usize, |
| 51 | /// First thirty-two bits of the fractional parts of the square |
| 52 | /// roots of the first eight prime numbers. |
| 53 | const iv256 = Iv32{ |
| 54 | 0x6A09E667, |
| 55 | 0xBB67AE85, |
| 56 | 0x3C6EF372, |
| 57 | 0xA54FF53A, |
| 58 | 0x510E527F, |
| 59 | 0x9B05688C, |
| 60 | 0x1F83D9AB, |
| 61 | 0x5BE0CD19, |
| 46 | 62 | }; |
| 47 | 63 | |
| 48 | | const Sha224Params = Sha2Params32{ |
| 49 | | .iv0 = 0xC1059ED8, |
| 50 | | .iv1 = 0x367CD507, |
| 51 | | .iv2 = 0x3070DD17, |
| 52 | | .iv3 = 0xF70E5939, |
| 53 | | .iv4 = 0xFFC00B31, |
| 54 | | .iv5 = 0x68581511, |
| 55 | | .iv6 = 0x64F98FA7, |
| 56 | | .iv7 = 0xBEFA4FA4, |
| 57 | | .digest_bits = 224, |
| 64 | /// First sixty-four bits of the fractional parts of the square |
| 65 | /// roots of the ninth through sixteenth prime numbers. |
| 66 | const iv384 = Iv64{ |
| 67 | 0xCBBB9D5DC1059ED8, |
| 68 | 0x629A292A367CD507, |
| 69 | 0x9159015A3070DD17, |
| 70 | 0x152FECD8F70E5939, |
| 71 | 0x67332667FFC00B31, |
| 72 | 0x8EB44A8768581511, |
| 73 | 0xDB0C2E0D64F98FA7, |
| 74 | 0x47B5481DBEFA4FA4, |
| 58 | 75 | }; |
| 59 | | |
| 60 | | const Sha256Params = Sha2Params32{ |
| 61 | | .iv0 = 0x6A09E667, |
| 62 | | .iv1 = 0xBB67AE85, |
| 63 | | .iv2 = 0x3C6EF372, |
| 64 | | .iv3 = 0xA54FF53A, |
| 65 | | .iv4 = 0x510E527F, |
| 66 | | .iv5 = 0x9B05688C, |
| 67 | | .iv6 = 0x1F83D9AB, |
| 68 | | .iv7 = 0x5BE0CD19, |
| 69 | | .digest_bits = 256, |
| 76 | /// First sixty-four bits of the fractional parts of the square |
| 77 | /// roots of the first eight prime numbers. |
| 78 | const iv512 = Iv64{ |
| 79 | 0x6A09E667F3BCC908, |
| 80 | 0xBB67AE8584CAA73B, |
| 81 | 0x3C6EF372FE94F82B, |
| 82 | 0xA54FF53A5F1D36F1, |
| 83 | 0x510E527FADE682D1, |
| 84 | 0x9B05688C2B3E6C1F, |
| 85 | 0x1F83D9ABFB41BD6B, |
| 86 | 0x5BE0CD19137E2179, |
| 70 | 87 | }; |
| 71 | 88 | |
| 72 | | const v4u32 = @Vector(4, u32); |
| 73 | | |
| 74 | | /// SHA-224 |
| 75 | | pub const Sha224 = Sha2x32(Sha224Params); |
| 76 | | |
| 77 | | /// SHA-256 |
| 78 | | pub const Sha256 = Sha2x32(Sha256Params); |
| 79 | | |
| 80 | | fn Sha2x32(comptime params: Sha2Params32) type { |
| 89 | const Iv32 = [8]u32; |
| 90 | fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type { |
| 81 | 91 | return struct { |
| 82 | 92 | const Self = @This(); |
| 83 | 93 | pub const block_length = 64; |
| 84 | | pub const digest_length = params.digest_bits / 8; |
| 94 | pub const digest_length = digest_bits / 8; |
| 85 | 95 | pub const Options = struct {}; |
| 86 | 96 | |
| 87 | 97 | s: [8]u32 align(16), |
| ... | ... | @@ -92,18 +102,7 @@ fn Sha2x32(comptime params: Sha2Params32) type { |
| 92 | 102 | |
| 93 | 103 | pub fn init(options: Options) Self { |
| 94 | 104 | _ = options; |
| 95 | | return Self{ |
| 96 | | .s = [_]u32{ |
| 97 | | params.iv0, |
| 98 | | params.iv1, |
| 99 | | params.iv2, |
| 100 | | params.iv3, |
| 101 | | params.iv4, |
| 102 | | params.iv5, |
| 103 | | params.iv6, |
| 104 | | params.iv7, |
| 105 | | }, |
| 106 | | }; |
| 105 | return Self{ .s = iv }; |
| 107 | 106 | } |
| 108 | 107 | |
| 109 | 108 | pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void { |
| ... | ... | @@ -167,8 +166,8 @@ fn Sha2x32(comptime params: Sha2Params32) type { |
| 167 | 166 | |
| 168 | 167 | d.round(&d.buf); |
| 169 | 168 | |
| 170 | | // May truncate for possible 224 output |
| 171 | | const rr = d.s[0 .. params.digest_bits / 32]; |
| 169 | // May truncate for possible 224 or 192 output |
| 170 | const rr = d.s[0 .. digest_length / 4]; |
| 172 | 171 | |
| 173 | 172 | for (rr, 0..) |s, j| { |
| 174 | 173 | mem.writeInt(u32, out[4 * j ..][0..4], s, .big); |
| ... | ... | @@ -199,11 +198,12 @@ fn Sha2x32(comptime params: Sha2Params32) type { |
| 199 | 198 | } |
| 200 | 199 | |
| 201 | 200 | if (!@inComptime()) { |
| 201 | const V4u32 = @Vector(4, u32); |
| 202 | 202 | switch (builtin.cpu.arch) { |
| 203 | 203 | .aarch64 => if (builtin.zig_backend != .stage2_c and comptime std.Target.aarch64.featureSetHas(builtin.cpu.features, .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)); |
| 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 | 207 | |
| 208 | 208 | comptime var k: u8 = 0; |
| 209 | 209 | inline while (k < 16) : (k += 1) { |
| ... | ... | @@ -211,7 +211,7 @@ fn Sha2x32(comptime params: Sha2Params32) type { |
| 211 | 211 | s_v[k] = asm ( |
| 212 | 212 | \\sha256su0.4s %[w0_3], %[w4_7] |
| 213 | 213 | \\sha256su1.4s %[w0_3], %[w8_11], %[w12_15] |
| 214 | | : [w0_3] "=w" (-> v4u32), |
| 214 | : [w0_3] "=w" (-> V4u32), |
| 215 | 215 | : [_] "0" (s_v[k - 4]), |
| 216 | 216 | [w4_7] "w" (s_v[k - 3]), |
| 217 | 217 | [w8_11] "w" (s_v[k - 2]), |
| ... | ... | @@ -219,7 +219,7 @@ fn Sha2x32(comptime params: Sha2Params32) type { |
| 219 | 219 | ); |
| 220 | 220 | } |
| 221 | 221 | |
| 222 | | const w: v4u32 = s_v[k] +% @as(v4u32, W[4 * k ..][0..4].*); |
| 222 | const w: V4u32 = s_v[k] +% @as(V4u32, W[4 * k ..][0..4].*); |
| 223 | 223 | asm volatile ( |
| 224 | 224 | \\mov.4s v0, %[x] |
| 225 | 225 | \\sha256h.4s %[x], %[y], %[w] |
| ... | ... | @@ -233,15 +233,15 @@ fn Sha2x32(comptime params: Sha2Params32) type { |
| 233 | 233 | ); |
| 234 | 234 | } |
| 235 | 235 | |
| 236 | | d.s[0..4].* = x +% @as(v4u32, d.s[0..4].*); |
| 237 | | d.s[4..8].* = y +% @as(v4u32, d.s[4..8].*); |
| 236 | d.s[0..4].* = x +% @as(V4u32, d.s[0..4].*); |
| 237 | d.s[4..8].* = y +% @as(V4u32, d.s[4..8].*); |
| 238 | 238 | return; |
| 239 | 239 | }, |
| 240 | 240 | // C backend doesn't currently support passing vectors to inline asm. |
| 241 | 241 | .x86_64 => if (builtin.zig_backend != .stage2_c and comptime std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sha, .avx2 })) { |
| 242 | | var x: v4u32 = [_]u32{ d.s[5], d.s[4], d.s[1], d.s[0] }; |
| 243 | | var y: v4u32 = [_]u32{ d.s[7], d.s[6], d.s[3], d.s[2] }; |
| 244 | | const s_v = @as(*[16]v4u32, @ptrCast(&s)); |
| 242 | var x: V4u32 = [_]u32{ d.s[5], d.s[4], d.s[1], d.s[0] }; |
| 243 | var y: V4u32 = [_]u32{ d.s[7], d.s[6], d.s[3], d.s[2] }; |
| 244 | const s_v = @as(*[16]V4u32, @ptrCast(&s)); |
| 245 | 245 | |
| 246 | 246 | comptime var k: u8 = 0; |
| 247 | 247 | inline while (k < 16) : (k += 1) { |
| ... | ... | @@ -253,7 +253,7 @@ fn Sha2x32(comptime params: Sha2Params32) type { |
| 253 | 253 | \\ paddd %[tmp], %[result] |
| 254 | 254 | \\ sha256msg2 %[w12_15], %[result] |
| 255 | 255 | : [tmp] "=&x" (tmp), |
| 256 | | [result] "=&x" (-> v4u32), |
| 256 | [result] "=&x" (-> V4u32), |
| 257 | 257 | : [_] "0" (tmp), |
| 258 | 258 | [w4_7] "x" (s_v[k + 1]), |
| 259 | 259 | [w8_11] "x" (s_v[k + 2]), |
| ... | ... | @@ -261,19 +261,19 @@ fn Sha2x32(comptime params: Sha2Params32) type { |
| 261 | 261 | ); |
| 262 | 262 | } |
| 263 | 263 | |
| 264 | | const w: v4u32 = s_v[k] +% @as(v4u32, W[4 * k ..][0..4].*); |
| 264 | const w: V4u32 = s_v[k] +% @as(V4u32, W[4 * k ..][0..4].*); |
| 265 | 265 | y = asm ("sha256rnds2 %[x], %[y]" |
| 266 | | : [y] "=x" (-> v4u32), |
| 266 | : [y] "=x" (-> V4u32), |
| 267 | 267 | : [_] "0" (y), |
| 268 | 268 | [x] "x" (x), |
| 269 | 269 | [_] "{xmm0}" (w), |
| 270 | 270 | ); |
| 271 | 271 | |
| 272 | 272 | x = asm ("sha256rnds2 %[y], %[x]" |
| 273 | | : [x] "=x" (-> v4u32), |
| 273 | : [x] "=x" (-> V4u32), |
| 274 | 274 | : [_] "0" (x), |
| 275 | 275 | [y] "x" (y), |
| 276 | | [_] "{xmm0}" (@as(v4u32, @bitCast(@as(u128, @bitCast(w)) >> 64))), |
| 276 | [_] "{xmm0}" (@as(V4u32, @bitCast(@as(u128, @bitCast(w)) >> 64))), |
| 277 | 277 | ); |
| 278 | 278 | } |
| 279 | 279 | |
| ... | ... | @@ -296,16 +296,7 @@ fn Sha2x32(comptime params: Sha2Params32) type { |
| 296 | 296 | 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)); |
| 297 | 297 | } |
| 298 | 298 | |
| 299 | | var v: [8]u32 = [_]u32{ |
| 300 | | d.s[0], |
| 301 | | d.s[1], |
| 302 | | d.s[2], |
| 303 | | d.s[3], |
| 304 | | d.s[4], |
| 305 | | d.s[5], |
| 306 | | d.s[6], |
| 307 | | d.s[7], |
| 308 | | }; |
| 299 | var v: [8]u32 = d.s; |
| 309 | 300 | |
| 310 | 301 | const round0 = comptime [_]RoundParam256{ |
| 311 | 302 | roundParam256(0, 1, 2, 3, 4, 5, 6, 7, 0), |
| ... | ... | @@ -381,14 +372,7 @@ fn Sha2x32(comptime params: Sha2Params32) type { |
| 381 | 372 | 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])); |
| 382 | 373 | } |
| 383 | 374 | |
| 384 | | d.s[0] +%= v[0]; |
| 385 | | d.s[1] +%= v[1]; |
| 386 | | d.s[2] +%= v[2]; |
| 387 | | d.s[3] +%= v[3]; |
| 388 | | d.s[4] +%= v[4]; |
| 389 | | d.s[5] +%= v[5]; |
| 390 | | d.s[6] +%= v[6]; |
| 391 | | d.s[7] +%= v[7]; |
| 375 | for (&d.s, v) |*dv, vv| dv.* +%= vv; |
| 392 | 376 | } |
| 393 | 377 | |
| 394 | 378 | pub const Error = error{}; |
| ... | ... | @@ -405,7 +389,33 @@ fn Sha2x32(comptime params: Sha2Params32) type { |
| 405 | 389 | }; |
| 406 | 390 | } |
| 407 | 391 | |
| 408 | | test "sha224 single" { |
| 392 | const RoundParam256 = struct { |
| 393 | a: usize, |
| 394 | b: usize, |
| 395 | c: usize, |
| 396 | d: usize, |
| 397 | e: usize, |
| 398 | f: usize, |
| 399 | g: usize, |
| 400 | h: usize, |
| 401 | i: usize, |
| 402 | }; |
| 403 | |
| 404 | fn roundParam256(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize) RoundParam256 { |
| 405 | return RoundParam256{ |
| 406 | .a = a, |
| 407 | .b = b, |
| 408 | .c = c, |
| 409 | .d = d, |
| 410 | .e = e, |
| 411 | .f = f, |
| 412 | .g = g, |
| 413 | .h = h, |
| 414 | .i = i, |
| 415 | }; |
| 416 | } |
| 417 | |
| 418 | test Sha224 { |
| 409 | 419 | try htest.assertEqualHash(Sha224, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""); |
| 410 | 420 | try htest.assertEqualHash(Sha224, "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc"); |
| 411 | 421 | try htest.assertEqualHash(Sha224, "c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| ... | ... | @@ -431,12 +441,18 @@ test "sha224 streaming" { |
| 431 | 441 | try htest.assertEqual("23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", out[0..]); |
| 432 | 442 | } |
| 433 | 443 | |
| 434 | | test "sha256 single" { |
| 444 | test Sha256 { |
| 435 | 445 | try htest.assertEqualHash(Sha256, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""); |
| 436 | 446 | try htest.assertEqualHash(Sha256, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc"); |
| 437 | 447 | try htest.assertEqualHash(Sha256, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 438 | 448 | } |
| 439 | 449 | |
| 450 | test Sha256T192 { |
| 451 | try htest.assertEqualHash(Sha256T192, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934c", ""); |
| 452 | try htest.assertEqualHash(Sha256T192, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9c", "abc"); |
| 453 | try htest.assertEqualHash(Sha256T192, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 454 | } |
| 455 | |
| 440 | 456 | test "sha256 streaming" { |
| 441 | 457 | var h = Sha256.init(.{}); |
| 442 | 458 | var out: [32]u8 = undefined; |
| ... | ... | @@ -466,132 +482,15 @@ test "sha256 aligned final" { |
| 466 | 482 | h.final(out[0..]); |
| 467 | 483 | } |
| 468 | 484 | |
| 469 | | ///////////////////// |
| 470 | | // Sha384 + Sha512 |
| 471 | | |
| 472 | | const RoundParam512 = struct { |
| 473 | | a: usize, |
| 474 | | b: usize, |
| 475 | | c: usize, |
| 476 | | d: usize, |
| 477 | | e: usize, |
| 478 | | f: usize, |
| 479 | | g: usize, |
| 480 | | h: usize, |
| 481 | | i: usize, |
| 482 | | k: u64, |
| 483 | | }; |
| 484 | | |
| 485 | | fn roundParam512(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize, k: u64) RoundParam512 { |
| 486 | | return RoundParam512{ |
| 487 | | .a = a, |
| 488 | | .b = b, |
| 489 | | .c = c, |
| 490 | | .d = d, |
| 491 | | .e = e, |
| 492 | | .f = f, |
| 493 | | .g = g, |
| 494 | | .h = h, |
| 495 | | .i = i, |
| 496 | | .k = k, |
| 497 | | }; |
| 498 | | } |
| 499 | | |
| 500 | | const Sha2Params64 = struct { |
| 501 | | iv0: u64, |
| 502 | | iv1: u64, |
| 503 | | iv2: u64, |
| 504 | | iv3: u64, |
| 505 | | iv4: u64, |
| 506 | | iv5: u64, |
| 507 | | iv6: u64, |
| 508 | | iv7: u64, |
| 509 | | digest_bits: usize, |
| 510 | | }; |
| 511 | | |
| 512 | | const Sha384Params = Sha2Params64{ |
| 513 | | .iv0 = 0xCBBB9D5DC1059ED8, |
| 514 | | .iv1 = 0x629A292A367CD507, |
| 515 | | .iv2 = 0x9159015A3070DD17, |
| 516 | | .iv3 = 0x152FECD8F70E5939, |
| 517 | | .iv4 = 0x67332667FFC00B31, |
| 518 | | .iv5 = 0x8EB44A8768581511, |
| 519 | | .iv6 = 0xDB0C2E0D64F98FA7, |
| 520 | | .iv7 = 0x47B5481DBEFA4FA4, |
| 521 | | .digest_bits = 384, |
| 522 | | }; |
| 523 | | |
| 524 | | const Sha512Params = Sha2Params64{ |
| 525 | | .iv0 = 0x6A09E667F3BCC908, |
| 526 | | .iv1 = 0xBB67AE8584CAA73B, |
| 527 | | .iv2 = 0x3C6EF372FE94F82B, |
| 528 | | .iv3 = 0xA54FF53A5F1D36F1, |
| 529 | | .iv4 = 0x510E527FADE682D1, |
| 530 | | .iv5 = 0x9B05688C2B3E6C1F, |
| 531 | | .iv6 = 0x1F83D9ABFB41BD6B, |
| 532 | | .iv7 = 0x5BE0CD19137E2179, |
| 533 | | .digest_bits = 512, |
| 534 | | }; |
| 535 | | |
| 536 | | const Sha512224Params = Sha2Params64{ |
| 537 | | .iv0 = 0x8C3D37C819544DA2, |
| 538 | | .iv1 = 0x73E1996689DCD4D6, |
| 539 | | .iv2 = 0x1DFAB7AE32FF9C82, |
| 540 | | .iv3 = 0x679DD514582F9FCF, |
| 541 | | .iv4 = 0x0F6D2B697BD44DA8, |
| 542 | | .iv5 = 0x77E36F7304C48942, |
| 543 | | .iv6 = 0x3F9D85A86A1D36C8, |
| 544 | | .iv7 = 0x1112E6AD91D692A1, |
| 545 | | .digest_bits = 224, |
| 546 | | }; |
| 547 | | |
| 548 | | const Sha512256Params = Sha2Params64{ |
| 549 | | .iv0 = 0x22312194FC2BF72C, |
| 550 | | .iv1 = 0x9F555FA3C84C64C2, |
| 551 | | .iv2 = 0x2393B86B6F53B151, |
| 552 | | .iv3 = 0x963877195940EABD, |
| 553 | | .iv4 = 0x96283EE2A88EFFE3, |
| 554 | | .iv5 = 0xBE5E1E2553863992, |
| 555 | | .iv6 = 0x2B0199FC2C85B8AA, |
| 556 | | .iv7 = 0x0EB72DDC81C52CA2, |
| 557 | | .digest_bits = 256, |
| 558 | | }; |
| 559 | | |
| 560 | | const Sha512T256Params = Sha2Params64{ |
| 561 | | .iv0 = 0x6A09E667F3BCC908, |
| 562 | | .iv1 = 0xBB67AE8584CAA73B, |
| 563 | | .iv2 = 0x3C6EF372FE94F82B, |
| 564 | | .iv3 = 0xA54FF53A5F1D36F1, |
| 565 | | .iv4 = 0x510E527FADE682D1, |
| 566 | | .iv5 = 0x9B05688C2B3E6C1F, |
| 567 | | .iv6 = 0x1F83D9ABFB41BD6B, |
| 568 | | .iv7 = 0x5BE0CD19137E2179, |
| 569 | | .digest_bits = 256, |
| 570 | | }; |
| 571 | | |
| 572 | | /// SHA-384 |
| 573 | | pub const Sha384 = Sha2x64(Sha384Params); |
| 574 | | |
| 575 | | /// SHA-512 |
| 576 | | pub const Sha512 = Sha2x64(Sha512Params); |
| 577 | | |
| 578 | | /// SHA-512/224 |
| 579 | | pub const Sha512224 = Sha2x64(Sha512224Params); |
| 580 | | |
| 581 | | /// SHA-512/256 |
| 582 | | pub const Sha512256 = Sha2x64(Sha512256Params); |
| 583 | | |
| 584 | | /// Truncated SHA-512 |
| 585 | | pub const Sha512T256 = Sha2x64(Sha512T256Params); |
| 586 | | |
| 587 | | fn Sha2x64(comptime params: Sha2Params64) type { |
| 485 | const Iv64 = [8]u64; |
| 486 | fn Sha2x64(comptime iv: Iv64, digest_bits: comptime_int) type { |
| 588 | 487 | return struct { |
| 589 | 488 | const Self = @This(); |
| 590 | 489 | pub const block_length = 128; |
| 591 | | pub const digest_length = params.digest_bits / 8; |
| 490 | pub const digest_length = std.math.divCeil(comptime_int, digest_bits, 8) catch unreachable; |
| 592 | 491 | pub const Options = struct {}; |
| 593 | 492 | |
| 594 | | s: [8]u64, |
| 493 | s: Iv64, |
| 595 | 494 | // Streaming Cache |
| 596 | 495 | buf: [128]u8 = undefined, |
| 597 | 496 | buf_len: u8 = 0, |
| ... | ... | @@ -599,18 +498,7 @@ fn Sha2x64(comptime params: Sha2Params64) type { |
| 599 | 498 | |
| 600 | 499 | pub fn init(options: Options) Self { |
| 601 | 500 | _ = options; |
| 602 | | return Self{ |
| 603 | | .s = [_]u64{ |
| 604 | | params.iv0, |
| 605 | | params.iv1, |
| 606 | | params.iv2, |
| 607 | | params.iv3, |
| 608 | | params.iv4, |
| 609 | | params.iv5, |
| 610 | | params.iv6, |
| 611 | | params.iv7, |
| 612 | | }, |
| 613 | | }; |
| 501 | return Self{ .s = iv }; |
| 614 | 502 | } |
| 615 | 503 | |
| 616 | 504 | pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void { |
| ... | ... | @@ -675,18 +563,19 @@ fn Sha2x64(comptime params: Sha2Params64) type { |
| 675 | 563 | d.round(d.buf[0..]); |
| 676 | 564 | |
| 677 | 565 | // May truncate for possible 384 output |
| 678 | | const rr = d.s[0 .. params.digest_bits / 64]; |
| 566 | const rr = d.s[0 .. digest_length / 8]; |
| 679 | 567 | |
| 680 | 568 | for (rr, 0..) |s, j| { |
| 681 | 569 | mem.writeInt(u64, out[8 * j ..][0..8], s, .big); |
| 682 | 570 | } |
| 683 | 571 | |
| 684 | | const bytes_left = params.digest_bits / 8 % 8; |
| 572 | if (digest_bits % 8 != 0) @compileError("impl doesn't support non-byte digest_len"); |
| 573 | const bytes_left = digest_bits / 8 % 8; |
| 685 | 574 | if (bytes_left > 0) { |
| 686 | | const rest = d.s[(params.digest_bits / 64)]; |
| 575 | const rest = d.s[(digest_bits / 64)]; |
| 687 | 576 | var buf: [8]u8 = undefined; |
| 688 | 577 | std.mem.writeInt(u64, &buf, rest, .big); |
| 689 | | @memcpy(out[params.digest_bits / 64 * 8 ..], buf[0..bytes_left]); |
| 578 | @memcpy(out[digest_bits / 64 * 8 ..], buf[0..bytes_left]); |
| 690 | 579 | } |
| 691 | 580 | } |
| 692 | 581 | |
| ... | ... | @@ -709,16 +598,7 @@ fn Sha2x64(comptime params: Sha2Params64) type { |
| 709 | 598 | (math.rotr(u64, s[i - 2], @as(u64, 19)) ^ math.rotr(u64, s[i - 2], @as(u64, 61)) ^ (s[i - 2] >> 6)); |
| 710 | 599 | } |
| 711 | 600 | |
| 712 | | var v: [8]u64 = [_]u64{ |
| 713 | | d.s[0], |
| 714 | | d.s[1], |
| 715 | | d.s[2], |
| 716 | | d.s[3], |
| 717 | | d.s[4], |
| 718 | | d.s[5], |
| 719 | | d.s[6], |
| 720 | | d.s[7], |
| 721 | | }; |
| 601 | var v: [8]u64 = d.s; |
| 722 | 602 | |
| 723 | 603 | const round0 = comptime [_]RoundParam512{ |
| 724 | 604 | roundParam512(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98D728AE22), |
| ... | ... | @@ -810,19 +690,94 @@ fn Sha2x64(comptime params: Sha2Params64) type { |
| 810 | 690 | 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])); |
| 811 | 691 | } |
| 812 | 692 | |
| 813 | | d.s[0] +%= v[0]; |
| 814 | | d.s[1] +%= v[1]; |
| 815 | | d.s[2] +%= v[2]; |
| 816 | | d.s[3] +%= v[3]; |
| 817 | | d.s[4] +%= v[4]; |
| 818 | | d.s[5] +%= v[5]; |
| 819 | | d.s[6] +%= v[6]; |
| 820 | | d.s[7] +%= v[7]; |
| 693 | for (&d.s, v) |*dv, vv| dv.* +%= vv; |
| 821 | 694 | } |
| 822 | 695 | }; |
| 823 | 696 | } |
| 824 | 697 | |
| 825 | | test "sha384 single" { |
| 698 | const RoundParam512 = struct { |
| 699 | a: usize, |
| 700 | b: usize, |
| 701 | c: usize, |
| 702 | d: usize, |
| 703 | e: usize, |
| 704 | f: usize, |
| 705 | g: usize, |
| 706 | h: usize, |
| 707 | i: usize, |
| 708 | k: u64, |
| 709 | }; |
| 710 | |
| 711 | fn roundParam512(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize, k: u64) RoundParam512 { |
| 712 | return RoundParam512{ |
| 713 | .a = a, |
| 714 | .b = b, |
| 715 | .c = c, |
| 716 | .d = d, |
| 717 | .e = e, |
| 718 | .f = f, |
| 719 | .g = g, |
| 720 | .h = h, |
| 721 | .i = i, |
| 722 | .k = k, |
| 723 | }; |
| 724 | } |
| 725 | |
| 726 | /// Compute the IV for a truncated version of SHA512 per FIPS 180 Section 5.3.6 |
| 727 | fn truncatedSha512Iv(digest_len: comptime_int) Iv64 { |
| 728 | const assert = std.debug.assert; |
| 729 | comptime assert(digest_len > 1); |
| 730 | comptime assert(digest_len <= 512); |
| 731 | comptime assert(digest_len != 384); // NIST specially defines this (see `iv384`) |
| 732 | |
| 733 | comptime var gen_params = iv512; |
| 734 | inline for (&gen_params) |*iv| { |
| 735 | iv.* ^= 0xa5a5a5a5a5a5a5a5; |
| 736 | } |
| 737 | const GenHash = Sha2x64(gen_params, 512); |
| 738 | |
| 739 | var params: [@sizeOf(Iv64)]u8 = undefined; |
| 740 | const algo_str = std.fmt.comptimePrint("SHA-512/{d}", .{digest_len}); |
| 741 | GenHash.hash(algo_str, &params, .{}); |
| 742 | |
| 743 | return Iv64{ |
| 744 | std.mem.readInt(u64, params[0..8], .big), |
| 745 | std.mem.readInt(u64, params[8..16], .big), |
| 746 | std.mem.readInt(u64, params[16..24], .big), |
| 747 | std.mem.readInt(u64, params[24..32], .big), |
| 748 | std.mem.readInt(u64, params[32..40], .big), |
| 749 | std.mem.readInt(u64, params[40..48], .big), |
| 750 | std.mem.readInt(u64, params[48..56], .big), |
| 751 | std.mem.readInt(u64, params[56..64], .big), |
| 752 | }; |
| 753 | } |
| 754 | |
| 755 | test truncatedSha512Iv { |
| 756 | // Section 5.3.6.1 |
| 757 | try std.testing.expectEqual(Iv64{ |
| 758 | 0x8C3D37C819544DA2, |
| 759 | 0x73E1996689DCD4D6, |
| 760 | 0x1DFAB7AE32FF9C82, |
| 761 | 0x679DD514582F9FCF, |
| 762 | 0x0F6D2B697BD44DA8, |
| 763 | 0x77E36F7304C48942, |
| 764 | 0x3F9D85A86A1D36C8, |
| 765 | 0x1112E6AD91D692A1, |
| 766 | }, truncatedSha512Iv(224)); |
| 767 | // Section 5.3.6.2 |
| 768 | try std.testing.expectEqual(Iv64{ |
| 769 | 0x22312194FC2BF72C, |
| 770 | 0x9F555FA3C84C64C2, |
| 771 | 0x2393B86B6F53B151, |
| 772 | 0x963877195940EABD, |
| 773 | 0x96283EE2A88EFFE3, |
| 774 | 0xBE5E1E2553863992, |
| 775 | 0x2B0199FC2C85B8AA, |
| 776 | 0x0EB72DDC81C52CA2, |
| 777 | }, truncatedSha512Iv(256)); |
| 778 | } |
| 779 | |
| 780 | test Sha384 { |
| 826 | 781 | const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"; |
| 827 | 782 | try htest.assertEqualHash(Sha384, h1, ""); |
| 828 | 783 | |
| ... | ... | @@ -856,7 +811,7 @@ test "sha384 streaming" { |
| 856 | 811 | try htest.assertEqual(h2, out[0..]); |
| 857 | 812 | } |
| 858 | 813 | |
| 859 | | test "sha512 single" { |
| 814 | test Sha512 { |
| 860 | 815 | const h1 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"; |
| 861 | 816 | try htest.assertEqualHash(Sha512, h1, ""); |
| 862 | 817 | |
| ... | ... | @@ -899,24 +854,24 @@ test "sha512 aligned final" { |
| 899 | 854 | h.final(out[0..]); |
| 900 | 855 | } |
| 901 | 856 | |
| 902 | | test "sha512-224 single" { |
| 857 | test Sha512_224 { |
| 903 | 858 | const h1 = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"; |
| 904 | | try htest.assertEqualHash(Sha512224, h1, ""); |
| 859 | try htest.assertEqualHash(Sha512_224, h1, ""); |
| 905 | 860 | |
| 906 | 861 | const h2 = "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"; |
| 907 | | try htest.assertEqualHash(Sha512224, h2, "abc"); |
| 862 | try htest.assertEqualHash(Sha512_224, h2, "abc"); |
| 908 | 863 | |
| 909 | 864 | const h3 = "23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9"; |
| 910 | | try htest.assertEqualHash(Sha512224, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 865 | try htest.assertEqualHash(Sha512_224, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 911 | 866 | } |
| 912 | 867 | |
| 913 | | test "sha512-256 single" { |
| 868 | test Sha512_256 { |
| 914 | 869 | const h1 = "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"; |
| 915 | | try htest.assertEqualHash(Sha512256, h1, ""); |
| 870 | try htest.assertEqualHash(Sha512_256, h1, ""); |
| 916 | 871 | |
| 917 | 872 | const h2 = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"; |
| 918 | | try htest.assertEqualHash(Sha512256, h2, "abc"); |
| 873 | try htest.assertEqualHash(Sha512_256, h2, "abc"); |
| 919 | 874 | |
| 920 | 875 | const h3 = "3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a"; |
| 921 | | try htest.assertEqualHash(Sha512256, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 876 | try htest.assertEqualHash(Sha512_256, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 922 | 877 | } |