| ... | ... | @@ -7,12 +7,11 @@ const builtin = @import("builtin"); |
| 7 | 7 | |
| 8 | 8 | const blake2 = crypto.hash.blake2; |
| 9 | 9 | const crypto = std.crypto; |
| 10 | const Io = std.Io; |
| 10 | 11 | const math = std.math; |
| 11 | 12 | const mem = std.mem; |
| 12 | 13 | const phc_format = pwhash.phc_format; |
| 13 | 14 | const pwhash = crypto.pwhash; |
| 14 | | |
| 15 | | const Thread = std.Thread; |
| 16 | 15 | const Blake2b512 = blake2.Blake2b512; |
| 17 | 16 | const Blocks = std.array_list.AlignedManaged([block_length]u64, .@"16"); |
| 18 | 17 | const H0 = [Blake2b512.digest_length + 8]u8; |
| ... | ... | @@ -204,20 +203,20 @@ fn initBlocks( |
| 204 | 203 | } |
| 205 | 204 | |
| 206 | 205 | fn processBlocks( |
| 207 | | allocator: mem.Allocator, |
| 208 | 206 | blocks: *Blocks, |
| 209 | 207 | time: u32, |
| 210 | 208 | memory: u32, |
| 211 | 209 | threads: u24, |
| 212 | 210 | mode: Mode, |
| 213 | | ) KdfError!void { |
| 211 | io: Io, |
| 212 | ) void { |
| 214 | 213 | const lanes = memory / threads; |
| 215 | 214 | const segments = lanes / sync_points; |
| 216 | 215 | |
| 217 | 216 | if (builtin.single_threaded or threads == 1) { |
| 218 | 217 | processBlocksSt(blocks, time, memory, threads, mode, lanes, segments); |
| 219 | 218 | } else { |
| 220 | | try processBlocksMt(allocator, blocks, time, memory, threads, mode, lanes, segments); |
| 219 | processBlocksMt(blocks, time, memory, threads, mode, lanes, segments, io); |
| 221 | 220 | } |
| 222 | 221 | } |
| 223 | 222 | |
| ... | ... | @@ -243,7 +242,6 @@ fn processBlocksSt( |
| 243 | 242 | } |
| 244 | 243 | |
| 245 | 244 | fn processBlocksMt( |
| 246 | | allocator: mem.Allocator, |
| 247 | 245 | blocks: *Blocks, |
| 248 | 246 | time: u32, |
| 249 | 247 | memory: u32, |
| ... | ... | @@ -251,26 +249,20 @@ fn processBlocksMt( |
| 251 | 249 | mode: Mode, |
| 252 | 250 | lanes: u32, |
| 253 | 251 | segments: u32, |
| 254 | | ) KdfError!void { |
| 255 | | var threads_list = try std.array_list.Managed(Thread).initCapacity(allocator, threads); |
| 256 | | defer threads_list.deinit(); |
| 257 | | |
| 252 | io: Io, |
| 253 | ) void { |
| 258 | 254 | var n: u32 = 0; |
| 259 | 255 | while (n < time) : (n += 1) { |
| 260 | 256 | var slice: u32 = 0; |
| 261 | 257 | while (slice < sync_points) : (slice += 1) { |
| 258 | var group: Io.Group = .init; |
| 262 | 259 | var lane: u24 = 0; |
| 263 | 260 | while (lane < threads) : (lane += 1) { |
| 264 | | const thread = try Thread.spawn(.{}, processSegment, .{ |
| 261 | group.async(io, processSegment, .{ |
| 265 | 262 | blocks, time, memory, threads, mode, lanes, segments, n, slice, lane, |
| 266 | 263 | }); |
| 267 | | threads_list.appendAssumeCapacity(thread); |
| 268 | | } |
| 269 | | lane = 0; |
| 270 | | while (lane < threads) : (lane += 1) { |
| 271 | | threads_list.items[lane].join(); |
| 272 | 264 | } |
| 273 | | threads_list.clearRetainingCapacity(); |
| 265 | group.wait(io); |
| 274 | 266 | } |
| 275 | 267 | } |
| 276 | 268 | } |
| ... | ... | @@ -489,6 +481,7 @@ pub fn kdf( |
| 489 | 481 | salt: []const u8, |
| 490 | 482 | params: Params, |
| 491 | 483 | mode: Mode, |
| 484 | io: Io, |
| 492 | 485 | ) KdfError!void { |
| 493 | 486 | if (derived_key.len < 4) return KdfError.WeakParameters; |
| 494 | 487 | if (derived_key.len > max_int) return KdfError.OutputTooLong; |
| ... | ... | @@ -510,7 +503,7 @@ pub fn kdf( |
| 510 | 503 | blocks.appendNTimesAssumeCapacity(@splat(0), memory); |
| 511 | 504 | |
| 512 | 505 | initBlocks(&blocks, &h0, memory, params.p); |
| 513 | | try processBlocks(allocator, &blocks, params.t, memory, params.p, mode); |
| 506 | processBlocks(&blocks, params.t, memory, params.p, mode, io); |
| 514 | 507 | finalize(&blocks, memory, params.p, derived_key); |
| 515 | 508 | } |
| 516 | 509 | |
| ... | ... | @@ -533,6 +526,7 @@ const PhcFormatHasher = struct { |
| 533 | 526 | params: Params, |
| 534 | 527 | mode: Mode, |
| 535 | 528 | buf: []u8, |
| 529 | io: Io, |
| 536 | 530 | ) HasherError![]const u8 { |
| 537 | 531 | if (params.secret != null or params.ad != null) return HasherError.InvalidEncoding; |
| 538 | 532 | |
| ... | ... | @@ -540,7 +534,7 @@ const PhcFormatHasher = struct { |
| 540 | 534 | crypto.random.bytes(&salt); |
| 541 | 535 | |
| 542 | 536 | var hash: [default_hash_len]u8 = undefined; |
| 543 | | try kdf(allocator, &hash, password, &salt, params, mode); |
| 537 | try kdf(allocator, &hash, password, &salt, params, mode, io); |
| 544 | 538 | |
| 545 | 539 | return phc_format.serialize(HashResult{ |
| 546 | 540 | .alg_id = @tagName(mode), |
| ... | ... | @@ -557,6 +551,7 @@ const PhcFormatHasher = struct { |
| 557 | 551 | allocator: mem.Allocator, |
| 558 | 552 | str: []const u8, |
| 559 | 553 | password: []const u8, |
| 554 | io: Io, |
| 560 | 555 | ) HasherError!void { |
| 561 | 556 | const hash_result = try phc_format.deserialize(HashResult, str); |
| 562 | 557 | |
| ... | ... | @@ -572,7 +567,7 @@ const PhcFormatHasher = struct { |
| 572 | 567 | if (expected_hash.len > hash_buf.len) return HasherError.InvalidEncoding; |
| 573 | 568 | const hash = hash_buf[0..expected_hash.len]; |
| 574 | 569 | |
| 575 | | try kdf(allocator, hash, password, hash_result.salt.constSlice(), params, mode); |
| 570 | try kdf(allocator, hash, password, hash_result.salt.constSlice(), params, mode, io); |
| 576 | 571 | if (!mem.eql(u8, hash, expected_hash)) return HasherError.PasswordVerificationFailed; |
| 577 | 572 | } |
| 578 | 573 | }; |
| ... | ... | @@ -595,6 +590,7 @@ pub fn strHash( |
| 595 | 590 | password: []const u8, |
| 596 | 591 | options: HashOptions, |
| 597 | 592 | out: []u8, |
| 593 | io: Io, |
| 598 | 594 | ) Error![]const u8 { |
| 599 | 595 | const allocator = options.allocator orelse return Error.AllocatorRequired; |
| 600 | 596 | switch (options.encoding) { |
| ... | ... | @@ -604,6 +600,7 @@ pub fn strHash( |
| 604 | 600 | options.params, |
| 605 | 601 | options.mode, |
| 606 | 602 | out, |
| 603 | io, |
| 607 | 604 | ), |
| 608 | 605 | .crypt => return Error.InvalidEncoding, |
| 609 | 606 | } |
| ... | ... | @@ -621,9 +618,10 @@ pub fn strVerify( |
| 621 | 618 | str: []const u8, |
| 622 | 619 | password: []const u8, |
| 623 | 620 | options: VerifyOptions, |
| 621 | io: Io, |
| 624 | 622 | ) Error!void { |
| 625 | 623 | const allocator = options.allocator orelse return Error.AllocatorRequired; |
| 626 | | return PhcFormatHasher.verify(allocator, str, password); |
| 624 | return PhcFormatHasher.verify(allocator, str, password, io); |
| 627 | 625 | } |
| 628 | 626 | |
| 629 | 627 | test "argon2d" { |
| ... | ... | @@ -640,6 +638,7 @@ test "argon2d" { |
| 640 | 638 | &salt, |
| 641 | 639 | .{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad }, |
| 642 | 640 | .argon2d, |
| 641 | std.testing.io, |
| 643 | 642 | ); |
| 644 | 643 | |
| 645 | 644 | const want = [_]u8{ |
| ... | ... | @@ -665,6 +664,7 @@ test "argon2i" { |
| 665 | 664 | &salt, |
| 666 | 665 | .{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad }, |
| 667 | 666 | .argon2i, |
| 667 | std.testing.io, |
| 668 | 668 | ); |
| 669 | 669 | |
| 670 | 670 | const want = [_]u8{ |
| ... | ... | @@ -690,6 +690,7 @@ test "argon2id" { |
| 690 | 690 | &salt, |
| 691 | 691 | .{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad }, |
| 692 | 692 | .argon2id, |
| 693 | std.testing.io, |
| 693 | 694 | ); |
| 694 | 695 | |
| 695 | 696 | const want = [_]u8{ |
| ... | ... | @@ -800,44 +801,44 @@ test "kdf" { |
| 800 | 801 | .{ |
| 801 | 802 | .mode = .argon2i, |
| 802 | 803 | .time = 4, |
| 803 | | .memory = 4096, |
| 804 | .memory = 256, |
| 804 | 805 | .threads = 4, |
| 805 | | .hash = "a11f7b7f3f93f02ad4bddb59ab62d121e278369288a0d0e7", |
| 806 | .hash = "f7dbbacbf16999e3700817a7e06f65a8db2e9fa9504ede4c", |
| 806 | 807 | }, |
| 807 | 808 | .{ |
| 808 | 809 | .mode = .argon2d, |
| 809 | 810 | .time = 4, |
| 810 | | .memory = 4096, |
| 811 | .memory = 256, |
| 811 | 812 | .threads = 4, |
| 812 | | .hash = "935598181aa8dc2b720914aa6435ac8d3e3a4210c5b0fb2d", |
| 813 | .hash = "ea2970501cf49faa5ba1d2e6370204e9b57ca90a8fea937b", |
| 813 | 814 | }, |
| 814 | 815 | .{ |
| 815 | 816 | .mode = .argon2id, |
| 816 | 817 | .time = 4, |
| 817 | | .memory = 4096, |
| 818 | .memory = 256, |
| 818 | 819 | .threads = 4, |
| 819 | | .hash = "145db9733a9f4ee43edf33c509be96b934d505a4efb33c5a", |
| 820 | .hash = "fbd40d5a8cb92f88c20bda4b3cdb1f9d5af1efa937032410", |
| 820 | 821 | }, |
| 821 | 822 | .{ |
| 822 | 823 | .mode = .argon2i, |
| 823 | 824 | .time = 4, |
| 824 | | .memory = 1024, |
| 825 | .memory = 256, |
| 825 | 826 | .threads = 8, |
| 826 | | .hash = "0cdd3956aa35e6b475a7b0c63488822f774f15b43f6e6e17", |
| 827 | .hash = "15d3c398364e53f68fd12d19baf3f21432d964254fe27467", |
| 827 | 828 | }, |
| 828 | 829 | .{ |
| 829 | 830 | .mode = .argon2d, |
| 830 | 831 | .time = 4, |
| 831 | | .memory = 1024, |
| 832 | .memory = 256, |
| 832 | 833 | .threads = 8, |
| 833 | | .hash = "83604fc2ad0589b9d055578f4d3cc55bc616df3578a896e9", |
| 834 | .hash = "23c9adc06f06e21e4612c1466a1be02627690932b02c0df0", |
| 834 | 835 | }, |
| 835 | 836 | .{ |
| 836 | 837 | .mode = .argon2id, |
| 837 | 838 | .time = 4, |
| 838 | | .memory = 1024, |
| 839 | .memory = 256, |
| 839 | 840 | .threads = 8, |
| 840 | | .hash = "8dafa8e004f8ea96bf7c0f93eecf67a6047476143d15577f", |
| 841 | .hash = "f22802f8ca47be93f9954e4ce20c1e944e938fbd4a125d9d", |
| 841 | 842 | }, |
| 842 | 843 | .{ |
| 843 | 844 | .mode = .argon2i, |
| ... | ... | @@ -863,23 +864,23 @@ test "kdf" { |
| 863 | 864 | .{ |
| 864 | 865 | .mode = .argon2i, |
| 865 | 866 | .time = 3, |
| 866 | | .memory = 1024, |
| 867 | .memory = 256, |
| 867 | 868 | .threads = 6, |
| 868 | | .hash = "d236b29c2b2a09babee842b0dec6aa1e83ccbdea8023dced", |
| 869 | .hash = "ebc8f91964abd8ceab49a12963b0a9e57d635bfa2aad2884", |
| 869 | 870 | }, |
| 870 | 871 | .{ |
| 871 | 872 | .mode = .argon2d, |
| 872 | 873 | .time = 3, |
| 873 | | .memory = 1024, |
| 874 | .memory = 256, |
| 874 | 875 | .threads = 6, |
| 875 | | .hash = "a3351b0319a53229152023d9206902f4ef59661cdca89481", |
| 876 | .hash = "1dd7202fd68da6675f769f4034b7a1db30d8785331954117", |
| 876 | 877 | }, |
| 877 | 878 | .{ |
| 878 | 879 | .mode = .argon2id, |
| 879 | 880 | .time = 3, |
| 880 | | .memory = 1024, |
| 881 | .memory = 256, |
| 881 | 882 | .threads = 6, |
| 882 | | .hash = "1640b932f4b60e272f5d2207b9a9c626ffa1bd88d2349016", |
| 883 | .hash = "424436b6ee22a66b04b9d0cf78f190305c5c166bae8baa09", |
| 883 | 884 | }, |
| 884 | 885 | }; |
| 885 | 886 | for (test_vectors) |v| { |
| ... | ... | @@ -894,6 +895,7 @@ test "kdf" { |
| 894 | 895 | salt, |
| 895 | 896 | .{ .t = v.time, .m = v.memory, .p = v.threads }, |
| 896 | 897 | v.mode, |
| 898 | std.testing.io, |
| 897 | 899 | ); |
| 898 | 900 | |
| 899 | 901 | try std.testing.expectEqualSlices(u8, &dk, &want); |
| ... | ... | @@ -903,6 +905,7 @@ test "kdf" { |
| 903 | 905 | test "phc format hasher" { |
| 904 | 906 | const allocator = std.testing.allocator; |
| 905 | 907 | const password = "testpass"; |
| 908 | const io = std.testing.io; |
| 906 | 909 | |
| 907 | 910 | var buf: [128]u8 = undefined; |
| 908 | 911 | const hash = try PhcFormatHasher.create( |
| ... | ... | @@ -911,25 +914,29 @@ test "phc format hasher" { |
| 911 | 914 | .{ .t = 3, .m = 32, .p = 4 }, |
| 912 | 915 | .argon2id, |
| 913 | 916 | &buf, |
| 917 | io, |
| 914 | 918 | ); |
| 915 | | try PhcFormatHasher.verify(allocator, hash, password); |
| 919 | try PhcFormatHasher.verify(allocator, hash, password, io); |
| 916 | 920 | } |
| 917 | 921 | |
| 918 | 922 | test "password hash and password verify" { |
| 919 | 923 | const allocator = std.testing.allocator; |
| 920 | 924 | const password = "testpass"; |
| 925 | const io = std.testing.io; |
| 921 | 926 | |
| 922 | 927 | var buf: [128]u8 = undefined; |
| 923 | 928 | const hash = try strHash( |
| 924 | 929 | password, |
| 925 | 930 | .{ .allocator = allocator, .params = .{ .t = 3, .m = 32, .p = 4 } }, |
| 926 | 931 | &buf, |
| 932 | io, |
| 927 | 933 | ); |
| 928 | | try strVerify(hash, password, .{ .allocator = allocator }); |
| 934 | try strVerify(hash, password, .{ .allocator = allocator }, io); |
| 929 | 935 | } |
| 930 | 936 | |
| 931 | 937 | test "kdf derived key length" { |
| 932 | 938 | const allocator = std.testing.allocator; |
| 939 | const io = std.testing.io; |
| 933 | 940 | |
| 934 | 941 | const password = "testpass"; |
| 935 | 942 | const salt = "saltsalt"; |
| ... | ... | @@ -937,11 +944,11 @@ test "kdf derived key length" { |
| 937 | 944 | const mode = Mode.argon2id; |
| 938 | 945 | |
| 939 | 946 | var dk1: [11]u8 = undefined; |
| 940 | | try kdf(allocator, &dk1, password, salt, params, mode); |
| 947 | try kdf(allocator, &dk1, password, salt, params, mode, io); |
| 941 | 948 | |
| 942 | 949 | var dk2: [77]u8 = undefined; |
| 943 | | try kdf(allocator, &dk2, password, salt, params, mode); |
| 950 | try kdf(allocator, &dk2, password, salt, params, mode, io); |
| 944 | 951 | |
| 945 | 952 | var dk3: [111]u8 = undefined; |
| 946 | | try kdf(allocator, &dk3, password, salt, params, mode); |
| 953 | try kdf(allocator, &dk3, password, salt, params, mode, io); |
| 947 | 954 | } |