authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2025-12-02 23:03:52+01:00
committergravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2025-12-02 23:03:52+01:00
log6fe95c28cf0909b4ea172c700a0df6416385cf94
treeddece819d0a585a0ab7596410a2f09dc4baeb6ac
parent95f93a0b281e32583edef36808231a5f61fb7de1

Argon2: use the std.Io interface

Also reduce the memory required by tests. 4GB for every test is way too much and doesn't provide much benefits in testing the algorithms.

2 files changed, 62 insertions(+), 46 deletions(-)

lib/std/crypto/argon2.zig+51-44
...@@ -7,12 +7,11 @@ const builtin = @import("builtin");...@@ -7,12 +7,11 @@ const builtin = @import("builtin");
77
8const blake2 = crypto.hash.blake2;8const blake2 = crypto.hash.blake2;
9const crypto = std.crypto;9const crypto = std.crypto;
10const Io = std.Io;
10const math = std.math;11const math = std.math;
11const mem = std.mem;12const mem = std.mem;
12const phc_format = pwhash.phc_format;13const phc_format = pwhash.phc_format;
13const pwhash = crypto.pwhash;14const pwhash = crypto.pwhash;
14
15const Thread = std.Thread;
16const Blake2b512 = blake2.Blake2b512;15const Blake2b512 = blake2.Blake2b512;
17const Blocks = std.array_list.AlignedManaged([block_length]u64, .@"16");16const Blocks = std.array_list.AlignedManaged([block_length]u64, .@"16");
18const H0 = [Blake2b512.digest_length + 8]u8;17const H0 = [Blake2b512.digest_length + 8]u8;
...@@ -204,20 +203,20 @@ fn initBlocks(...@@ -204,20 +203,20 @@ fn initBlocks(
204}203}
205204
206fn processBlocks(205fn processBlocks(
207 allocator: mem.Allocator,
208 blocks: *Blocks,206 blocks: *Blocks,
209 time: u32,207 time: u32,
210 memory: u32,208 memory: u32,
211 threads: u24,209 threads: u24,
212 mode: Mode,210 mode: Mode,
213) KdfError!void {211 io: Io,
212) void {
214 const lanes = memory / threads;213 const lanes = memory / threads;
215 const segments = lanes / sync_points;214 const segments = lanes / sync_points;
216215
217 if (builtin.single_threaded or threads == 1) {216 if (builtin.single_threaded or threads == 1) {
218 processBlocksSt(blocks, time, memory, threads, mode, lanes, segments);217 processBlocksSt(blocks, time, memory, threads, mode, lanes, segments);
219 } else {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}
223222
...@@ -243,7 +242,6 @@ fn processBlocksSt(...@@ -243,7 +242,6 @@ fn processBlocksSt(
243}242}
244243
245fn processBlocksMt(244fn processBlocksMt(
246 allocator: mem.Allocator,
247 blocks: *Blocks,245 blocks: *Blocks,
248 time: u32,246 time: u32,
249 memory: u32,247 memory: u32,
...@@ -251,26 +249,20 @@ fn processBlocksMt(...@@ -251,26 +249,20 @@ fn processBlocksMt(
251 mode: Mode,249 mode: Mode,
252 lanes: u32,250 lanes: u32,
253 segments: u32,251 segments: u32,
254) KdfError!void {252 io: Io,
255 var threads_list = try std.array_list.Managed(Thread).initCapacity(allocator, threads);253) void {
256 defer threads_list.deinit();
257
258 var n: u32 = 0;254 var n: u32 = 0;
259 while (n < time) : (n += 1) {255 while (n < time) : (n += 1) {
260 var slice: u32 = 0;256 var slice: u32 = 0;
261 while (slice < sync_points) : (slice += 1) {257 while (slice < sync_points) : (slice += 1) {
258 var group: Io.Group = .init;
262 var lane: u24 = 0;259 var lane: u24 = 0;
263 while (lane < threads) : (lane += 1) {260 while (lane < threads) : (lane += 1) {
264 const thread = try Thread.spawn(.{}, processSegment, .{261 group.async(io, processSegment, .{
265 blocks, time, memory, threads, mode, lanes, segments, n, slice, lane,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,6 +481,7 @@ pub fn kdf(
489 salt: []const u8,481 salt: []const u8,
490 params: Params,482 params: Params,
491 mode: Mode,483 mode: Mode,
484 io: Io,
492) KdfError!void {485) KdfError!void {
493 if (derived_key.len < 4) return KdfError.WeakParameters;486 if (derived_key.len < 4) return KdfError.WeakParameters;
494 if (derived_key.len > max_int) return KdfError.OutputTooLong;487 if (derived_key.len > max_int) return KdfError.OutputTooLong;
...@@ -510,7 +503,7 @@ pub fn kdf(...@@ -510,7 +503,7 @@ pub fn kdf(
510 blocks.appendNTimesAssumeCapacity(@splat(0), memory);503 blocks.appendNTimesAssumeCapacity(@splat(0), memory);
511504
512 initBlocks(&blocks, &h0, memory, params.p);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 finalize(&blocks, memory, params.p, derived_key);507 finalize(&blocks, memory, params.p, derived_key);
515}508}
516509
...@@ -533,6 +526,7 @@ const PhcFormatHasher = struct {...@@ -533,6 +526,7 @@ const PhcFormatHasher = struct {
533 params: Params,526 params: Params,
534 mode: Mode,527 mode: Mode,
535 buf: []u8,528 buf: []u8,
529 io: Io,
536 ) HasherError![]const u8 {530 ) HasherError![]const u8 {
537 if (params.secret != null or params.ad != null) return HasherError.InvalidEncoding;531 if (params.secret != null or params.ad != null) return HasherError.InvalidEncoding;
538532
...@@ -540,7 +534,7 @@ const PhcFormatHasher = struct {...@@ -540,7 +534,7 @@ const PhcFormatHasher = struct {
540 crypto.random.bytes(&salt);534 crypto.random.bytes(&salt);
541535
542 var hash: [default_hash_len]u8 = undefined;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);
544538
545 return phc_format.serialize(HashResult{539 return phc_format.serialize(HashResult{
546 .alg_id = @tagName(mode),540 .alg_id = @tagName(mode),
...@@ -557,6 +551,7 @@ const PhcFormatHasher = struct {...@@ -557,6 +551,7 @@ const PhcFormatHasher = struct {
557 allocator: mem.Allocator,551 allocator: mem.Allocator,
558 str: []const u8,552 str: []const u8,
559 password: []const u8,553 password: []const u8,
554 io: Io,
560 ) HasherError!void {555 ) HasherError!void {
561 const hash_result = try phc_format.deserialize(HashResult, str);556 const hash_result = try phc_format.deserialize(HashResult, str);
562557
...@@ -572,7 +567,7 @@ const PhcFormatHasher = struct {...@@ -572,7 +567,7 @@ const PhcFormatHasher = struct {
572 if (expected_hash.len > hash_buf.len) return HasherError.InvalidEncoding;567 if (expected_hash.len > hash_buf.len) return HasherError.InvalidEncoding;
573 const hash = hash_buf[0..expected_hash.len];568 const hash = hash_buf[0..expected_hash.len];
574569
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 if (!mem.eql(u8, hash, expected_hash)) return HasherError.PasswordVerificationFailed;571 if (!mem.eql(u8, hash, expected_hash)) return HasherError.PasswordVerificationFailed;
577 }572 }
578};573};
...@@ -595,6 +590,7 @@ pub fn strHash(...@@ -595,6 +590,7 @@ pub fn strHash(
595 password: []const u8,590 password: []const u8,
596 options: HashOptions,591 options: HashOptions,
597 out: []u8,592 out: []u8,
593 io: Io,
598) Error![]const u8 {594) Error![]const u8 {
599 const allocator = options.allocator orelse return Error.AllocatorRequired;595 const allocator = options.allocator orelse return Error.AllocatorRequired;
600 switch (options.encoding) {596 switch (options.encoding) {
...@@ -604,6 +600,7 @@ pub fn strHash(...@@ -604,6 +600,7 @@ pub fn strHash(
604 options.params,600 options.params,
605 options.mode,601 options.mode,
606 out,602 out,
603 io,
607 ),604 ),
608 .crypt => return Error.InvalidEncoding,605 .crypt => return Error.InvalidEncoding,
609 }606 }
...@@ -621,9 +618,10 @@ pub fn strVerify(...@@ -621,9 +618,10 @@ pub fn strVerify(
621 str: []const u8,618 str: []const u8,
622 password: []const u8,619 password: []const u8,
623 options: VerifyOptions,620 options: VerifyOptions,
621 io: Io,
624) Error!void {622) Error!void {
625 const allocator = options.allocator orelse return Error.AllocatorRequired;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}
628626
629test "argon2d" {627test "argon2d" {
...@@ -640,6 +638,7 @@ test "argon2d" {...@@ -640,6 +638,7 @@ test "argon2d" {
640 &salt,638 &salt,
641 .{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad },639 .{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad },
642 .argon2d,640 .argon2d,
641 std.testing.io,
643 );642 );
644643
645 const want = [_]u8{644 const want = [_]u8{
...@@ -665,6 +664,7 @@ test "argon2i" {...@@ -665,6 +664,7 @@ test "argon2i" {
665 &salt,664 &salt,
666 .{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad },665 .{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad },
667 .argon2i,666 .argon2i,
667 std.testing.io,
668 );668 );
669669
670 const want = [_]u8{670 const want = [_]u8{
...@@ -690,6 +690,7 @@ test "argon2id" {...@@ -690,6 +690,7 @@ test "argon2id" {
690 &salt,690 &salt,
691 .{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad },691 .{ .t = 3, .m = 32, .p = 4, .secret = &secret, .ad = &ad },
692 .argon2id,692 .argon2id,
693 std.testing.io,
693 );694 );
694695
695 const want = [_]u8{696 const want = [_]u8{
...@@ -800,44 +801,44 @@ test "kdf" {...@@ -800,44 +801,44 @@ test "kdf" {
800 .{801 .{
801 .mode = .argon2i,802 .mode = .argon2i,
802 .time = 4,803 .time = 4,
803 .memory = 4096,804 .memory = 256,
804 .threads = 4,805 .threads = 4,
805 .hash = "a11f7b7f3f93f02ad4bddb59ab62d121e278369288a0d0e7",806 .hash = "f7dbbacbf16999e3700817a7e06f65a8db2e9fa9504ede4c",
806 },807 },
807 .{808 .{
808 .mode = .argon2d,809 .mode = .argon2d,
809 .time = 4,810 .time = 4,
810 .memory = 4096,811 .memory = 256,
811 .threads = 4,812 .threads = 4,
812 .hash = "935598181aa8dc2b720914aa6435ac8d3e3a4210c5b0fb2d",813 .hash = "ea2970501cf49faa5ba1d2e6370204e9b57ca90a8fea937b",
813 },814 },
814 .{815 .{
815 .mode = .argon2id,816 .mode = .argon2id,
816 .time = 4,817 .time = 4,
817 .memory = 4096,818 .memory = 256,
818 .threads = 4,819 .threads = 4,
819 .hash = "145db9733a9f4ee43edf33c509be96b934d505a4efb33c5a",820 .hash = "fbd40d5a8cb92f88c20bda4b3cdb1f9d5af1efa937032410",
820 },821 },
821 .{822 .{
822 .mode = .argon2i,823 .mode = .argon2i,
823 .time = 4,824 .time = 4,
824 .memory = 1024,825 .memory = 256,
825 .threads = 8,826 .threads = 8,
826 .hash = "0cdd3956aa35e6b475a7b0c63488822f774f15b43f6e6e17",827 .hash = "15d3c398364e53f68fd12d19baf3f21432d964254fe27467",
827 },828 },
828 .{829 .{
829 .mode = .argon2d,830 .mode = .argon2d,
830 .time = 4,831 .time = 4,
831 .memory = 1024,832 .memory = 256,
832 .threads = 8,833 .threads = 8,
833 .hash = "83604fc2ad0589b9d055578f4d3cc55bc616df3578a896e9",834 .hash = "23c9adc06f06e21e4612c1466a1be02627690932b02c0df0",
834 },835 },
835 .{836 .{
836 .mode = .argon2id,837 .mode = .argon2id,
837 .time = 4,838 .time = 4,
838 .memory = 1024,839 .memory = 256,
839 .threads = 8,840 .threads = 8,
840 .hash = "8dafa8e004f8ea96bf7c0f93eecf67a6047476143d15577f",841 .hash = "f22802f8ca47be93f9954e4ce20c1e944e938fbd4a125d9d",
841 },842 },
842 .{843 .{
843 .mode = .argon2i,844 .mode = .argon2i,
...@@ -863,23 +864,23 @@ test "kdf" {...@@ -863,23 +864,23 @@ test "kdf" {
863 .{864 .{
864 .mode = .argon2i,865 .mode = .argon2i,
865 .time = 3,866 .time = 3,
866 .memory = 1024,867 .memory = 256,
867 .threads = 6,868 .threads = 6,
868 .hash = "d236b29c2b2a09babee842b0dec6aa1e83ccbdea8023dced",869 .hash = "ebc8f91964abd8ceab49a12963b0a9e57d635bfa2aad2884",
869 },870 },
870 .{871 .{
871 .mode = .argon2d,872 .mode = .argon2d,
872 .time = 3,873 .time = 3,
873 .memory = 1024,874 .memory = 256,
874 .threads = 6,875 .threads = 6,
875 .hash = "a3351b0319a53229152023d9206902f4ef59661cdca89481",876 .hash = "1dd7202fd68da6675f769f4034b7a1db30d8785331954117",
876 },877 },
877 .{878 .{
878 .mode = .argon2id,879 .mode = .argon2id,
879 .time = 3,880 .time = 3,
880 .memory = 1024,881 .memory = 256,
881 .threads = 6,882 .threads = 6,
882 .hash = "1640b932f4b60e272f5d2207b9a9c626ffa1bd88d2349016",883 .hash = "424436b6ee22a66b04b9d0cf78f190305c5c166bae8baa09",
883 },884 },
884 };885 };
885 for (test_vectors) |v| {886 for (test_vectors) |v| {
...@@ -894,6 +895,7 @@ test "kdf" {...@@ -894,6 +895,7 @@ test "kdf" {
894 salt,895 salt,
895 .{ .t = v.time, .m = v.memory, .p = v.threads },896 .{ .t = v.time, .m = v.memory, .p = v.threads },
896 v.mode,897 v.mode,
898 std.testing.io,
897 );899 );
898900
899 try std.testing.expectEqualSlices(u8, &dk, &want);901 try std.testing.expectEqualSlices(u8, &dk, &want);
...@@ -903,6 +905,7 @@ test "kdf" {...@@ -903,6 +905,7 @@ test "kdf" {
903test "phc format hasher" {905test "phc format hasher" {
904 const allocator = std.testing.allocator;906 const allocator = std.testing.allocator;
905 const password = "testpass";907 const password = "testpass";
908 const io = std.testing.io;
906909
907 var buf: [128]u8 = undefined;910 var buf: [128]u8 = undefined;
908 const hash = try PhcFormatHasher.create(911 const hash = try PhcFormatHasher.create(
...@@ -911,25 +914,29 @@ test "phc format hasher" {...@@ -911,25 +914,29 @@ test "phc format hasher" {
911 .{ .t = 3, .m = 32, .p = 4 },914 .{ .t = 3, .m = 32, .p = 4 },
912 .argon2id,915 .argon2id,
913 &buf,916 &buf,
917 io,
914 );918 );
915 try PhcFormatHasher.verify(allocator, hash, password);919 try PhcFormatHasher.verify(allocator, hash, password, io);
916}920}
917921
918test "password hash and password verify" {922test "password hash and password verify" {
919 const allocator = std.testing.allocator;923 const allocator = std.testing.allocator;
920 const password = "testpass";924 const password = "testpass";
925 const io = std.testing.io;
921926
922 var buf: [128]u8 = undefined;927 var buf: [128]u8 = undefined;
923 const hash = try strHash(928 const hash = try strHash(
924 password,929 password,
925 .{ .allocator = allocator, .params = .{ .t = 3, .m = 32, .p = 4 } },930 .{ .allocator = allocator, .params = .{ .t = 3, .m = 32, .p = 4 } },
926 &buf,931 &buf,
932 io,
927 );933 );
928 try strVerify(hash, password, .{ .allocator = allocator });934 try strVerify(hash, password, .{ .allocator = allocator }, io);
929}935}
930936
931test "kdf derived key length" {937test "kdf derived key length" {
932 const allocator = std.testing.allocator;938 const allocator = std.testing.allocator;
939 const io = std.testing.io;
933940
934 const password = "testpass";941 const password = "testpass";
935 const salt = "saltsalt";942 const salt = "saltsalt";
...@@ -937,11 +944,11 @@ test "kdf derived key length" {...@@ -937,11 +944,11 @@ test "kdf derived key length" {
937 const mode = Mode.argon2id;944 const mode = Mode.argon2id;
938945
939 var dk1: [11]u8 = undefined;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);
941948
942 var dk2: [77]u8 = undefined;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);
944951
945 var dk3: [111]u8 = undefined;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}
lib/std/crypto/benchmark.zig+11-2
...@@ -450,6 +450,7 @@ fn benchmarkPwhash(...@@ -450,6 +450,7 @@ fn benchmarkPwhash(
450 comptime ty: anytype,450 comptime ty: anytype,
451 comptime params: *const anyopaque,451 comptime params: *const anyopaque,
452 comptime count: comptime_int,452 comptime count: comptime_int,
453 io: std.Io,
453) !f64 {454) !f64 {
454 const password = "testpass" ** 2;455 const password = "testpass" ** 2;
455 const opts = ty.HashOptions{456 const opts = ty.HashOptions{
...@@ -459,12 +460,20 @@ fn benchmarkPwhash(...@@ -459,12 +460,20 @@ fn benchmarkPwhash(
459 };460 };
460 var buf: [256]u8 = undefined;461 var buf: [256]u8 = undefined;
461462
463 const strHash = ty.strHash;
464 const strHashFnInfo = @typeInfo(@TypeOf(strHash)).@"fn";
465 const needs_io = strHashFnInfo.params.len == 4;
466
462 var timer = try Timer.start();467 var timer = try Timer.start();
463 const start = timer.lap();468 const start = timer.lap();
464 {469 {
465 var i: usize = 0;470 var i: usize = 0;
466 while (i < count) : (i += 1) {471 while (i < count) : (i += 1) {
467 _ = try ty.strHash(password, opts, &buf);472 if (needs_io) {
473 _ = try strHash(password, opts, &buf, io);
474 } else {
475 _ = try strHash(password, opts, &buf);
476 }
468 mem.doNotOptimizeAway(&buf);477 mem.doNotOptimizeAway(&buf);
469 }478 }
470 }479 }
...@@ -623,7 +632,7 @@ pub fn main() !void {...@@ -623,7 +632,7 @@ pub fn main() !void {
623632
624 inline for (pwhashes) |H| {633 inline for (pwhashes) |H| {
625 if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {634 if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
626 const throughput = try benchmarkPwhash(arena_allocator, H.ty, H.params, mode(64));635 const throughput = try benchmarkPwhash(arena_allocator, H.ty, H.params, mode(64), io);
627 try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput });636 try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput });
628 try stdout.flush();637 try stdout.flush();
629 }638 }