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