authorgravatar for clickingbuttons@pm.meclickingbuttons <clickingbuttons@pm.me> 2024-04-28 16:22:09-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-04-28 22:22:09+02:00
log8a36a1f913c729993d53786486b542c0501dc27d
treeb648a1b375e543737e019ba238d56a5d1a6e13d9
parent25f1526fe6424cef156724977b75a5b80a3d5833
signaturebadge-check Signed by PGP key B5690EEEBB952194

std.crypto.hash.sha2: cleanup add add more docs (#19744)

* 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. * std.crypto.sha2: make Sha512_224 and Sha512_256 pub * make generic type implementation detail, add comments * fix iv * address @jedisct1 feedback * fix typo * renaming * add truncation clarifying comment and Sha259T192 tests

1 files changed, 238 insertions(+), 283 deletions(-)

lib/std/crypto/sha2.zig+238-283
...@@ -1,87 +1,97 @@...@@ -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
1const std = @import("../std.zig");16const std = @import("../std.zig");
2const builtin = @import("builtin");17const builtin = @import("builtin");
3const mem = std.mem;18const mem = std.mem;
4const math = std.math;19const math = std.math;
5const htest = @import("test.zig");20const htest = @import("test.zig");
621
7/////////////////////22pub const Sha224 = Sha2x32(iv224, 224);
8// Sha224 + Sha25623pub const Sha256 = Sha2x32(iv256, 256);
924pub const Sha384 = Sha2x64(iv384, 384);
10const RoundParam256 = struct {25pub const Sha512 = Sha2x64(iv512, 512);
11 a: usize,26
12 b: usize,27/// SHA-256 truncated to leftmost 192 bits.
13 c: usize,28pub const Sha256T192 = Sha2x32(iv256, 192);
14 d: usize,29
15 e: usize,30/// SHA-512 truncated to leftmost 224 bits.
16 f: usize,31pub const Sha512T224 = Sha2x64(iv512, 224);
17 g: usize,32/// SHA-512 truncated to leftmost 256 bits.
18 h: usize,33pub const Sha512T256 = Sha2x64(iv512, 256);
19 i: usize,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,
20};50};
2151/// First thirty-two bits of the fractional parts of the square
22fn roundParam256(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize) RoundParam256 {52/// roots of the first eight prime numbers.
23 return RoundParam256{53const iv256 = Iv32{
24 .a = a,54 0x6A09E667,
25 .b = b,55 0xBB67AE85,
26 .c = c,56 0x3C6EF372,
27 .d = d,57 0xA54FF53A,
28 .e = e,58 0x510E527F,
29 .f = f,59 0x9B05688C,
30 .g = g,60 0x1F83D9AB,
31 .h = h,61 0x5BE0CD19,
32 .i = i,
33 };
34}
35
36const 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,
46};62};
4763
48const Sha224Params = Sha2Params32{64/// First sixty-four bits of the fractional parts of the square
49 .iv0 = 0xC1059ED8,65/// roots of the ninth through sixteenth prime numbers.
50 .iv1 = 0x367CD507,66const iv384 = Iv64{
51 .iv2 = 0x3070DD17,67 0xCBBB9D5DC1059ED8,
52 .iv3 = 0xF70E5939,68 0x629A292A367CD507,
53 .iv4 = 0xFFC00B31,69 0x9159015A3070DD17,
54 .iv5 = 0x68581511,70 0x152FECD8F70E5939,
55 .iv6 = 0x64F98FA7,71 0x67332667FFC00B31,
56 .iv7 = 0xBEFA4FA4,72 0x8EB44A8768581511,
57 .digest_bits = 224,73 0xDB0C2E0D64F98FA7,
74 0x47B5481DBEFA4FA4,
58};75};
5976/// First sixty-four bits of the fractional parts of the square
60const Sha256Params = Sha2Params32{77/// roots of the first eight prime numbers.
61 .iv0 = 0x6A09E667,78const iv512 = Iv64{
62 .iv1 = 0xBB67AE85,79 0x6A09E667F3BCC908,
63 .iv2 = 0x3C6EF372,80 0xBB67AE8584CAA73B,
64 .iv3 = 0xA54FF53A,81 0x3C6EF372FE94F82B,
65 .iv4 = 0x510E527F,82 0xA54FF53A5F1D36F1,
66 .iv5 = 0x9B05688C,83 0x510E527FADE682D1,
67 .iv6 = 0x1F83D9AB,84 0x9B05688C2B3E6C1F,
68 .iv7 = 0x5BE0CD19,85 0x1F83D9ABFB41BD6B,
69 .digest_bits = 256,86 0x5BE0CD19137E2179,
70};87};
7188
72const v4u32 = @Vector(4, u32);89const Iv32 = [8]u32;
7390fn Sha2x32(comptime iv: Iv32, digest_bits: comptime_int) type {
74/// SHA-224
75pub const Sha224 = Sha2x32(Sha224Params);
76
77/// SHA-256
78pub const Sha256 = Sha2x32(Sha256Params);
79
80fn Sha2x32(comptime params: Sha2Params32) type {
81 return struct {91 return struct {
82 const Self = @This();92 const Self = @This();
83 pub const block_length = 64;93 pub const block_length = 64;
84 pub const digest_length = params.digest_bits / 8;94 pub const digest_length = digest_bits / 8;
85 pub const Options = struct {};95 pub const Options = struct {};
8696
87 s: [8]u32 align(16),97 s: [8]u32 align(16),
...@@ -92,18 +102,7 @@ fn Sha2x32(comptime params: Sha2Params32) type {...@@ -92,18 +102,7 @@ fn Sha2x32(comptime params: Sha2Params32) type {
92102
93 pub fn init(options: Options) Self {103 pub fn init(options: Options) Self {
94 _ = options;104 _ = options;
95 return Self{105 return Self{ .s = iv };
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 };
107 }106 }
108107
109 pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {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,8 +166,8 @@ fn Sha2x32(comptime params: Sha2Params32) type {
167166
168 d.round(&d.buf);167 d.round(&d.buf);
169168
170 // May truncate for possible 224 output169 // May truncate for possible 224 or 192 output
171 const rr = d.s[0 .. params.digest_bits / 32];170 const rr = d.s[0 .. digest_length / 4];
172171
173 for (rr, 0..) |s, j| {172 for (rr, 0..) |s, j| {
174 mem.writeInt(u32, out[4 * j ..][0..4], s, .big);173 mem.writeInt(u32, out[4 * j ..][0..4], s, .big);
...@@ -199,11 +198,12 @@ fn Sha2x32(comptime params: Sha2Params32) type {...@@ -199,11 +198,12 @@ fn Sha2x32(comptime params: Sha2Params32) type {
199 }198 }
200199
201 if (!@inComptime()) {200 if (!@inComptime()) {
201 const V4u32 = @Vector(4, u32);
202 switch (builtin.cpu.arch) {202 switch (builtin.cpu.arch) {
203 .aarch64 => if (builtin.zig_backend != .stage2_c and comptime std.Target.aarch64.featureSetHas(builtin.cpu.features, .sha2)) {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].*;204 var x: V4u32 = d.s[0..4].*;
205 var y: v4u32 = d.s[4..8].*;205 var y: V4u32 = d.s[4..8].*;
206 const s_v = @as(*[16]v4u32, @ptrCast(&s));206 const s_v = @as(*[16]V4u32, @ptrCast(&s));
207207
208 comptime var k: u8 = 0;208 comptime var k: u8 = 0;
209 inline while (k < 16) : (k += 1) {209 inline while (k < 16) : (k += 1) {
...@@ -211,7 +211,7 @@ fn Sha2x32(comptime params: Sha2Params32) type {...@@ -211,7 +211,7 @@ fn Sha2x32(comptime params: Sha2Params32) type {
211 s_v[k] = asm (211 s_v[k] = asm (
212 \\sha256su0.4s %[w0_3], %[w4_7]212 \\sha256su0.4s %[w0_3], %[w4_7]
213 \\sha256su1.4s %[w0_3], %[w8_11], %[w12_15]213 \\sha256su1.4s %[w0_3], %[w8_11], %[w12_15]
214 : [w0_3] "=w" (-> v4u32),214 : [w0_3] "=w" (-> V4u32),
215 : [_] "0" (s_v[k - 4]),215 : [_] "0" (s_v[k - 4]),
216 [w4_7] "w" (s_v[k - 3]),216 [w4_7] "w" (s_v[k - 3]),
217 [w8_11] "w" (s_v[k - 2]),217 [w8_11] "w" (s_v[k - 2]),
...@@ -219,7 +219,7 @@ fn Sha2x32(comptime params: Sha2Params32) type {...@@ -219,7 +219,7 @@ fn Sha2x32(comptime params: Sha2Params32) type {
219 );219 );
220 }220 }
221221
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 asm volatile (223 asm volatile (
224 \\mov.4s v0, %[x]224 \\mov.4s v0, %[x]
225 \\sha256h.4s %[x], %[y], %[w]225 \\sha256h.4s %[x], %[y], %[w]
...@@ -233,15 +233,15 @@ fn Sha2x32(comptime params: Sha2Params32) type {...@@ -233,15 +233,15 @@ fn Sha2x32(comptime params: Sha2Params32) type {
233 );233 );
234 }234 }
235235
236 d.s[0..4].* = x +% @as(v4u32, d.s[0..4].*);236 d.s[0..4].* = x +% @as(V4u32, d.s[0..4].*);
237 d.s[4..8].* = y +% @as(v4u32, d.s[4..8].*);237 d.s[4..8].* = y +% @as(V4u32, d.s[4..8].*);
238 return;238 return;
239 },239 },
240 // C backend doesn't currently support passing vectors to inline asm.240 // C backend doesn't currently support passing vectors to inline asm.
241 .x86_64 => if (builtin.zig_backend != .stage2_c and comptime std.Target.x86.featureSetHasAll(builtin.cpu.features, .{ .sha, .avx2 })) {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] };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] };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));244 const s_v = @as(*[16]V4u32, @ptrCast(&s));
245245
246 comptime var k: u8 = 0;246 comptime var k: u8 = 0;
247 inline while (k < 16) : (k += 1) {247 inline while (k < 16) : (k += 1) {
...@@ -253,7 +253,7 @@ fn Sha2x32(comptime params: Sha2Params32) type {...@@ -253,7 +253,7 @@ fn Sha2x32(comptime params: Sha2Params32) type {
253 \\ paddd %[tmp], %[result]253 \\ paddd %[tmp], %[result]
254 \\ sha256msg2 %[w12_15], %[result]254 \\ sha256msg2 %[w12_15], %[result]
255 : [tmp] "=&x" (tmp),255 : [tmp] "=&x" (tmp),
256 [result] "=&x" (-> v4u32),256 [result] "=&x" (-> V4u32),
257 : [_] "0" (tmp),257 : [_] "0" (tmp),
258 [w4_7] "x" (s_v[k + 1]),258 [w4_7] "x" (s_v[k + 1]),
259 [w8_11] "x" (s_v[k + 2]),259 [w8_11] "x" (s_v[k + 2]),
...@@ -261,19 +261,19 @@ fn Sha2x32(comptime params: Sha2Params32) type {...@@ -261,19 +261,19 @@ fn Sha2x32(comptime params: Sha2Params32) type {
261 );261 );
262 }262 }
263263
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 y = asm ("sha256rnds2 %[x], %[y]"265 y = asm ("sha256rnds2 %[x], %[y]"
266 : [y] "=x" (-> v4u32),266 : [y] "=x" (-> V4u32),
267 : [_] "0" (y),267 : [_] "0" (y),
268 [x] "x" (x),268 [x] "x" (x),
269 [_] "{xmm0}" (w),269 [_] "{xmm0}" (w),
270 );270 );
271271
272 x = asm ("sha256rnds2 %[y], %[x]"272 x = asm ("sha256rnds2 %[y], %[x]"
273 : [x] "=x" (-> v4u32),273 : [x] "=x" (-> V4u32),
274 : [_] "0" (x),274 : [_] "0" (x),
275 [y] "x" (y),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 }
279279
...@@ -296,16 +296,7 @@ fn Sha2x32(comptime params: Sha2Params32) type {...@@ -296,16 +296,7 @@ fn Sha2x32(comptime params: Sha2Params32) type {
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));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 }
298298
299 var v: [8]u32 = [_]u32{299 var v: [8]u32 = d.s;
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 };
309300
310 const round0 = comptime [_]RoundParam256{301 const round0 = comptime [_]RoundParam256{
311 roundParam256(0, 1, 2, 3, 4, 5, 6, 7, 0),302 roundParam256(0, 1, 2, 3, 4, 5, 6, 7, 0),
...@@ -381,14 +372,7 @@ fn Sha2x32(comptime params: Sha2Params32) type {...@@ -381,14 +372,7 @@ fn Sha2x32(comptime params: Sha2Params32) type {
381 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 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 }
383374
384 d.s[0] +%= v[0];375 for (&d.s, v) |*dv, vv| dv.* +%= vv;
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];
392 }376 }
393377
394 pub const Error = error{};378 pub const Error = error{};
...@@ -405,7 +389,33 @@ fn Sha2x32(comptime params: Sha2Params32) type {...@@ -405,7 +389,33 @@ fn Sha2x32(comptime params: Sha2Params32) type {
405 };389 };
406}390}
407391
408test "sha224 single" {392const 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
404fn 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
418test Sha224 {
409 try htest.assertEqualHash(Sha224, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", "");419 try htest.assertEqualHash(Sha224, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", "");
410 try htest.assertEqualHash(Sha224, "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc");420 try htest.assertEqualHash(Sha224, "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc");
411 try htest.assertEqualHash(Sha224, "c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");421 try htest.assertEqualHash(Sha224, "c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
...@@ -431,12 +441,18 @@ test "sha224 streaming" {...@@ -431,12 +441,18 @@ test "sha224 streaming" {
431 try htest.assertEqual("23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", out[0..]);441 try htest.assertEqual("23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", out[0..]);
432}442}
433443
434test "sha256 single" {444test Sha256 {
435 try htest.assertEqualHash(Sha256, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "");445 try htest.assertEqualHash(Sha256, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "");
436 try htest.assertEqualHash(Sha256, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc");446 try htest.assertEqualHash(Sha256, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc");
437 try htest.assertEqualHash(Sha256, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");447 try htest.assertEqualHash(Sha256, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
438}448}
439449
450test Sha256T192 {
451 try htest.assertEqualHash(Sha256T192, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934c", "");
452 try htest.assertEqualHash(Sha256T192, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9c", "abc");
453 try htest.assertEqualHash(Sha256T192, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
454}
455
440test "sha256 streaming" {456test "sha256 streaming" {
441 var h = Sha256.init(.{});457 var h = Sha256.init(.{});
442 var out: [32]u8 = undefined;458 var out: [32]u8 = undefined;
...@@ -466,132 +482,15 @@ test "sha256 aligned final" {...@@ -466,132 +482,15 @@ test "sha256 aligned final" {
466 h.final(out[0..]);482 h.final(out[0..]);
467}483}
468484
469/////////////////////485const Iv64 = [8]u64;
470// Sha384 + Sha512486fn Sha2x64(comptime iv: Iv64, digest_bits: comptime_int) type {
471
472const 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
485fn 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
500const 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
512const 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
524const 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
536const 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
548const 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
560const 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
573pub const Sha384 = Sha2x64(Sha384Params);
574
575/// SHA-512
576pub const Sha512 = Sha2x64(Sha512Params);
577
578/// SHA-512/224
579pub const Sha512224 = Sha2x64(Sha512224Params);
580
581/// SHA-512/256
582pub const Sha512256 = Sha2x64(Sha512256Params);
583
584/// Truncated SHA-512
585pub const Sha512T256 = Sha2x64(Sha512T256Params);
586
587fn Sha2x64(comptime params: Sha2Params64) type {
588 return struct {487 return struct {
589 const Self = @This();488 const Self = @This();
590 pub const block_length = 128;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 pub const Options = struct {};491 pub const Options = struct {};
593492
594 s: [8]u64,493 s: Iv64,
595 // Streaming Cache494 // Streaming Cache
596 buf: [128]u8 = undefined,495 buf: [128]u8 = undefined,
597 buf_len: u8 = 0,496 buf_len: u8 = 0,
...@@ -599,18 +498,7 @@ fn Sha2x64(comptime params: Sha2Params64) type {...@@ -599,18 +498,7 @@ fn Sha2x64(comptime params: Sha2Params64) type {
599498
600 pub fn init(options: Options) Self {499 pub fn init(options: Options) Self {
601 _ = options;500 _ = options;
602 return Self{501 return Self{ .s = iv };
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 };
614 }502 }
615503
616 pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {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,18 +563,19 @@ fn Sha2x64(comptime params: Sha2Params64) type {
675 d.round(d.buf[0..]);563 d.round(d.buf[0..]);
676564
677 // May truncate for possible 384 output565 // 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];
679567
680 for (rr, 0..) |s, j| {568 for (rr, 0..) |s, j| {
681 mem.writeInt(u64, out[8 * j ..][0..8], s, .big);569 mem.writeInt(u64, out[8 * j ..][0..8], s, .big);
682 }570 }
683571
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 if (bytes_left > 0) {574 if (bytes_left > 0) {
686 const rest = d.s[(params.digest_bits / 64)];575 const rest = d.s[(digest_bits / 64)];
687 var buf: [8]u8 = undefined;576 var buf: [8]u8 = undefined;
688 std.mem.writeInt(u64, &buf, rest, .big);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 }
692581
...@@ -709,16 +598,7 @@ fn Sha2x64(comptime params: Sha2Params64) type {...@@ -709,16 +598,7 @@ fn Sha2x64(comptime params: Sha2Params64) type {
709 (math.rotr(u64, s[i - 2], @as(u64, 19)) ^ math.rotr(u64, s[i - 2], @as(u64, 61)) ^ (s[i - 2] >> 6));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 }
711600
712 var v: [8]u64 = [_]u64{601 var v: [8]u64 = d.s;
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 };
722602
723 const round0 = comptime [_]RoundParam512{603 const round0 = comptime [_]RoundParam512{
724 roundParam512(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98D728AE22),604 roundParam512(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98D728AE22),
...@@ -810,19 +690,94 @@ fn Sha2x64(comptime params: Sha2Params64) type {...@@ -810,19 +690,94 @@ fn Sha2x64(comptime params: Sha2Params64) type {
810 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]));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 }
812692
813 d.s[0] +%= v[0];693 for (&d.s, v) |*dv, vv| dv.* +%= vv;
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];
821 }694 }
822 };695 };
823}696}
824697
825test "sha384 single" {698const 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
711fn 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
727fn 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
755test 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
780test Sha384 {
826 const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b";781 const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b";
827 try htest.assertEqualHash(Sha384, h1, "");782 try htest.assertEqualHash(Sha384, h1, "");
828783
...@@ -856,7 +811,7 @@ test "sha384 streaming" {...@@ -856,7 +811,7 @@ test "sha384 streaming" {
856 try htest.assertEqual(h2, out[0..]);811 try htest.assertEqual(h2, out[0..]);
857}812}
858813
859test "sha512 single" {814test Sha512 {
860 const h1 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e";815 const h1 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e";
861 try htest.assertEqualHash(Sha512, h1, "");816 try htest.assertEqualHash(Sha512, h1, "");
862817
...@@ -899,24 +854,24 @@ test "sha512 aligned final" {...@@ -899,24 +854,24 @@ test "sha512 aligned final" {
899 h.final(out[0..]);854 h.final(out[0..]);
900}855}
901856
902test "sha512-224 single" {857test Sha512_224 {
903 const h1 = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4";858 const h1 = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4";
904 try htest.assertEqualHash(Sha512224, h1, "");859 try htest.assertEqualHash(Sha512_224, h1, "");
905860
906 const h2 = "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa";861 const h2 = "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa";
907 try htest.assertEqualHash(Sha512224, h2, "abc");862 try htest.assertEqualHash(Sha512_224, h2, "abc");
908863
909 const h3 = "23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9";864 const h3 = "23fec5bb94d60b23308192640b0c453335d664734fe40e7268674af9";
910 try htest.assertEqualHash(Sha512224, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");865 try htest.assertEqualHash(Sha512_224, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
911}866}
912867
913test "sha512-256 single" {868test Sha512_256 {
914 const h1 = "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a";869 const h1 = "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a";
915 try htest.assertEqualHash(Sha512256, h1, "");870 try htest.assertEqualHash(Sha512_256, h1, "");
916871
917 const h2 = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23";872 const h2 = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23";
918 try htest.assertEqualHash(Sha512256, h2, "abc");873 try htest.assertEqualHash(Sha512_256, h2, "abc");
919874
920 const h3 = "3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a";875 const h3 = "3928e184fb8690f840da3988121d31be65cb9d3ef83ee6146feac861e19b563a";
921 try htest.assertEqualHash(Sha512256, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");876 try htest.assertEqualHash(Sha512_256, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
922}877}