authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-01-07 23:14:37+01:00
committergravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-01-07 23:14:37+01:00
log4a29a6e43265f37ac504e4f0a9c78f4917fcab65
treeca7de9460780df942fd7f0e88af5b3625007fc93
parent2153759a22f584c5ad43e15fdc13b1afcb5449a7

Add scrypt.createWithSalt


1 files changed, 62 insertions(+), 3 deletions(-)

lib/std/crypto/scrypt.zig+62-3
......@@ -421,16 +421,27 @@ const PhcFormatHasher = struct {
421421 ) HasherError![]const u8 {
422422 var salt: [default_salt_len]u8 = undefined;
423423 io.random(&salt);
424 return createWithSalt(allocator, password, params, buf, &salt);
425 }
424426
427 /// Return a deterministic hash of the password encoded as a PHC-format string.
428 /// Uses the provided salt instead of generating one randomly.
429 pub fn createWithSalt(
430 allocator: mem.Allocator,
431 password: []const u8,
432 params: Params,
433 buf: []u8,
434 salt: *const [default_salt_len]u8,
435 ) HasherError![]const u8 {
425436 var hash: [default_hash_len]u8 = undefined;
426 try kdf(allocator, &hash, password, &salt, params);
437 try kdf(allocator, &hash, password, salt, params);
427438
428439 return phc_format.serialize(HashResult{
429440 .alg_id = alg_id,
430441 .ln = params.ln,
431442 .r = params.r,
432443 .p = params.p,
433 .salt = try BinValue(max_salt_len).fromSlice(&salt),
444 .salt = try BinValue(max_salt_len).fromSlice(salt),
434445 .hash = try BinValue(max_hash_len).fromSlice(&hash),
435446 }, buf);
436447 }
......@@ -471,7 +482,19 @@ const CryptFormatHasher = struct {
471482 ) HasherError![]const u8 {
472483 var salt_bin: [default_salt_len]u8 = undefined;
473484 io.random(&salt_bin);
474 const salt = crypt_format.saltFromBin(salt_bin.len, salt_bin);
485 return createWithSalt(allocator, password, params, buf, &salt_bin);
486 }
487
488 /// Return a deterministic hash of the password encoded into the modular crypt format.
489 /// Uses the provided salt instead of generating one randomly.
490 pub fn createWithSalt(
491 allocator: mem.Allocator,
492 password: []const u8,
493 params: Params,
494 buf: []u8,
495 salt_bin: *const [default_salt_len]u8,
496 ) HasherError![]const u8 {
497 const salt = crypt_format.saltFromBin(salt_bin.len, salt_bin.*);
475498
476499 var hash: [default_hash_len]u8 = undefined;
477500 try kdf(allocator, &hash, password, &salt, params);
......@@ -526,6 +549,22 @@ pub fn strHash(
526549 }
527550}
528551
552/// Compute a deterministic hash of a password using the scrypt key derivation function.
553/// The function returns a string that includes all the parameters required for verification.
554/// Uses the provided salt instead of generating one randomly.
555pub fn strHashWithSalt(
556 password: []const u8,
557 options: HashOptions,
558 out: []u8,
559 salt: *const [default_salt_len]u8,
560) Error![]const u8 {
561 const allocator = options.allocator orelse return Error.AllocatorRequired;
562 switch (options.encoding) {
563 .phc => return PhcFormatHasher.createWithSalt(allocator, password, options.params, out, salt),
564 .crypt => return CryptFormatHasher.createWithSalt(allocator, password, options.params, out, salt),
565 }
566}
567
529568/// Options for hash verification.
530569///
531570/// Allocator is required for scrypt.
......@@ -728,3 +767,23 @@ test "kdf fast" {
728767 try std.testing.expectEqualSlices(u8, &dk, v.want);
729768 }
730769}
770
771test "strHashWithSalt deterministic" {
772 const alloc = std.testing.allocator;
773 const password = "testpass";
774 const salt: [default_salt_len]u8 = "0123456789abcdef0123456789abcdef".*;
775 const params: Params = .{ .ln = 1, .r = 1, .p = 1 };
776
777 var buf1: [128]u8 = undefined;
778 var buf2: [128]u8 = undefined;
779
780 const str1 = try strHashWithSalt(password, .{ .allocator = alloc, .params = params, .encoding = .phc }, &buf1, &salt);
781 const str2 = try strHashWithSalt(password, .{ .allocator = alloc, .params = params, .encoding = .phc }, &buf2, &salt);
782 try std.testing.expectEqualStrings(str1, str2);
783 try strVerify(str1, password, .{ .allocator = alloc });
784
785 const str3 = try strHashWithSalt(password, .{ .allocator = alloc, .params = params, .encoding = .crypt }, &buf1, &salt);
786 const str4 = try strHashWithSalt(password, .{ .allocator = alloc, .params = params, .encoding = .crypt }, &buf2, &salt);
787 try std.testing.expectEqualStrings(str3, str4);
788 try strVerify(str3, password, .{ .allocator = alloc });
789}