| ... | ... | @@ -421,16 +421,27 @@ const PhcFormatHasher = struct { |
| 421 | 421 | ) HasherError![]const u8 { |
| 422 | 422 | var salt: [default_salt_len]u8 = undefined; |
| 423 | 423 | io.random(&salt); |
| 424 | return createWithSalt(allocator, password, params, buf, &salt); |
| 425 | } |
| 424 | 426 | |
| 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 { |
| 425 | 436 | var hash: [default_hash_len]u8 = undefined; |
| 426 | | try kdf(allocator, &hash, password, &salt, params); |
| 437 | try kdf(allocator, &hash, password, salt, params); |
| 427 | 438 | |
| 428 | 439 | return phc_format.serialize(HashResult{ |
| 429 | 440 | .alg_id = alg_id, |
| 430 | 441 | .ln = params.ln, |
| 431 | 442 | .r = params.r, |
| 432 | 443 | .p = params.p, |
| 433 | | .salt = try BinValue(max_salt_len).fromSlice(&salt), |
| 444 | .salt = try BinValue(max_salt_len).fromSlice(salt), |
| 434 | 445 | .hash = try BinValue(max_hash_len).fromSlice(&hash), |
| 435 | 446 | }, buf); |
| 436 | 447 | } |
| ... | ... | @@ -471,7 +482,19 @@ const CryptFormatHasher = struct { |
| 471 | 482 | ) HasherError![]const u8 { |
| 472 | 483 | var salt_bin: [default_salt_len]u8 = undefined; |
| 473 | 484 | 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.*); |
| 475 | 498 | |
| 476 | 499 | var hash: [default_hash_len]u8 = undefined; |
| 477 | 500 | try kdf(allocator, &hash, password, &salt, params); |
| ... | ... | @@ -526,6 +549,22 @@ pub fn strHash( |
| 526 | 549 | } |
| 527 | 550 | } |
| 528 | 551 | |
| 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. |
| 555 | pub 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 | |
| 529 | 568 | /// Options for hash verification. |
| 530 | 569 | /// |
| 531 | 570 | /// Allocator is required for scrypt. |
| ... | ... | @@ -728,3 +767,23 @@ test "kdf fast" { |
| 728 | 767 | try std.testing.expectEqualSlices(u8, &dk, v.want); |
| 729 | 768 | } |
| 730 | 769 | } |
| 770 | |
| 771 | test "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 | } |