| ... | @@ -7,6 +7,7 @@ const math = std.math; | ... | @@ -7,6 +7,7 @@ const math = std.math; |
| 7 | const mem = std.mem; | 7 | const mem = std.mem; |
| 8 | const pwhash = crypto.pwhash; | 8 | const pwhash = crypto.pwhash; |
| 9 | const testing = std.testing; | 9 | const testing = std.testing; |
| | 10 | const HmacSha512 = crypto.auth.hmac.sha2.HmacSha512; |
| 10 | const Sha512 = crypto.hash.sha2.Sha512; | 11 | const Sha512 = crypto.hash.sha2.Sha512; |
| 11 | const utils = crypto.utils; | 12 | const utils = crypto.utils; |
| 12 | | 13 | |
| ... | @@ -405,10 +406,21 @@ pub const State = struct { | ... | @@ -405,10 +406,21 @@ pub const State = struct { |
| 405 | } | 406 | } |
| 406 | }; | 407 | }; |
| 407 | | 408 | |
| | 409 | /// bcrypt parameters |
| 408 | pub const Params = struct { | 410 | pub const Params = struct { |
| | 411 | /// log2 of the number of rounds |
| 409 | rounds_log: u6, | 412 | rounds_log: u6, |
| 410 | }; | 413 | }; |
| 411 | | 414 | |
| | 415 | /// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key stretching function. |
| | 416 | /// bcrypt is a computationally expensive and cache-hard function, explicitly designed to slow down exhaustive searches. |
| | 417 | /// |
| | 418 | /// The function returns the hash as a `dk_length` byte array, that doesn't include anything besides the hash output. |
| | 419 | /// |
| | 420 | /// For a generic key-derivation function, use `bcrypt.pbkdf()` instead. |
| | 421 | /// |
| | 422 | /// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes. |
| | 423 | /// If this is an issue for your application, use `bcryptWithoutTruncation` instead. |
| 412 | pub fn bcrypt( | 424 | pub fn bcrypt( |
| 413 | password: []const u8, | 425 | password: []const u8, |
| 414 | salt: [salt_length]u8, | 426 | salt: [salt_length]u8, |
| ... | @@ -443,6 +455,34 @@ pub fn bcrypt( | ... | @@ -443,6 +455,34 @@ pub fn bcrypt( |
| 443 | return ct[0..dk_length].*; | 455 | return ct[0..dk_length].*; |
| 444 | } | 456 | } |
| 445 | | 457 | |
| | 458 | /// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key stretching function. |
| | 459 | /// bcrypt is a computationally expensive and cache-hard function, explicitly designed to slow down exhaustive searches. |
| | 460 | /// |
| | 461 | /// The function returns the hash as a `dk_length` byte array, that doesn't include anything besides the hash output. |
| | 462 | /// |
| | 463 | /// For a generic key-derivation function, use `bcrypt.pbkdf()` instead. |
| | 464 | /// |
| | 465 | /// This function is identical to `bcrypt`, except that it doesn't silently truncate passwords. |
| | 466 | /// Instead, passwords longer than 72 bytes are pre-hashed using HMAC-SHA512 before being passed to bcrypt. |
| | 467 | pub fn bcryptWithoutTruncation( |
| | 468 | password: []const u8, |
| | 469 | salt: [salt_length]u8, |
| | 470 | params: Params, |
| | 471 | ) [dk_length]u8 { |
| | 472 | if (password.len <= 72) { |
| | 473 | return bcrypt(password, salt, params); |
| | 474 | } |
| | 475 | |
| | 476 | var pre_hash: [HmacSha512.mac_length]u8 = undefined; |
| | 477 | HmacSha512.create(&pre_hash, password, &salt); |
| | 478 | |
| | 479 | const Encoder = crypt_format.Codec.Encoder; |
| | 480 | var pre_hash_b64: [Encoder.calcSize(pre_hash.len)]u8 = undefined; |
| | 481 | _ = Encoder.encode(&pre_hash_b64, &pre_hash); |
| | 482 | |
| | 483 | return bcrypt(&pre_hash_b64, salt, params); |
| | 484 | } |
| | 485 | |
| 446 | const pbkdf_prf = struct { | 486 | const pbkdf_prf = struct { |
| 447 | const Self = @This(); | 487 | const Self = @This(); |
| 448 | pub const mac_length = 32; | 488 | pub const mac_length = 32; |
| ... | @@ -450,24 +490,24 @@ const pbkdf_prf = struct { | ... | @@ -450,24 +490,24 @@ const pbkdf_prf = struct { |
| 450 | hasher: Sha512, | 490 | hasher: Sha512, |
| 451 | sha2pass: [Sha512.digest_length]u8, | 491 | sha2pass: [Sha512.digest_length]u8, |
| 452 | | 492 | |
| 453 | pub fn create(out: *[mac_length]u8, msg: []const u8, key: []const u8) void { | 493 | fn create(out: *[mac_length]u8, msg: []const u8, key: []const u8) void { |
| 454 | var ctx = Self.init(key); | 494 | var ctx = Self.init(key); |
| 455 | ctx.update(msg); | 495 | ctx.update(msg); |
| 456 | ctx.final(out); | 496 | ctx.final(out); |
| 457 | } | 497 | } |
| 458 | | 498 | |
| 459 | pub fn init(key: []const u8) Self { | 499 | fn init(key: []const u8) Self { |
| 460 | var self: Self = undefined; | 500 | var self: Self = undefined; |
| 461 | self.hasher = Sha512.init(.{}); | 501 | self.hasher = Sha512.init(.{}); |
| 462 | Sha512.hash(key, &self.sha2pass, .{}); | 502 | Sha512.hash(key, &self.sha2pass, .{}); |
| 463 | return self; | 503 | return self; |
| 464 | } | 504 | } |
| 465 | | 505 | |
| 466 | pub fn update(self: *Self, msg: []const u8) void { | 506 | fn update(self: *Self, msg: []const u8) void { |
| 467 | self.hasher.update(msg); | 507 | self.hasher.update(msg); |
| 468 | } | 508 | } |
| 469 | | 509 | |
| 470 | pub fn final(self: *Self, out: *[mac_length]u8) void { | 510 | fn final(self: *Self, out: *[mac_length]u8) void { |
| 471 | var sha2salt: [Sha512.digest_length]u8 = undefined; | 511 | var sha2salt: [Sha512.digest_length]u8 = undefined; |
| 472 | self.hasher.final(&sha2salt); | 512 | self.hasher.final(&sha2salt); |
| 473 | out.* = hash(self.sha2pass, sha2salt); | 513 | out.* = hash(self.sha2pass, sha2salt); |
| ... | @@ -517,10 +557,12 @@ const pbkdf_prf = struct { | ... | @@ -517,10 +557,12 @@ const pbkdf_prf = struct { |
| 517 | } | 557 | } |
| 518 | }; | 558 | }; |
| 519 | | 559 | |
| 520 | /// bcrypt PBKDF2 implementation with variations to match OpenBSD | 560 | /// bcrypt-pbkdf is a key derivation function based on bcrypt. |
| 521 | /// https://github.com/openbsd/src/blob/6df1256b7792691e66c2ed9d86a8c103069f9e34/lib/libutil/bcrypt_pbkdf.c#L98 | 561 | /// This is the function used in OpenSSH to derive encryption keys from passphrases. |
| 522 | /// | 562 | /// |
| 523 | /// This particular variant is used in e.g. SSH | 563 | /// This implementation is compatible with the OpenBSD implementation (https://github.com/openbsd/src/blob/master/lib/libutil/bcrypt_pbkdf.c). |
| | 564 | /// |
| | 565 | /// Unlike the password hashing function `bcrypt`, this function doesn't silently truncate passwords longer than 72 bytes. |
| 524 | pub fn pbkdf(pass: []const u8, salt: []const u8, key: []u8, rounds: u32) !void { | 566 | pub fn pbkdf(pass: []const u8, salt: []const u8, key: []u8, rounds: u32) !void { |
| 525 | try crypto.pwhash.pbkdf2(key, pass, salt, rounds, pbkdf_prf); | 567 | try crypto.pwhash.pbkdf2(key, pass, salt, rounds, pbkdf_prf); |
| 526 | } | 568 | } |
| ... | @@ -540,8 +582,9 @@ const crypt_format = struct { | ... | @@ -540,8 +582,9 @@ const crypt_format = struct { |
| 540 | password: []const u8, | 582 | password: []const u8, |
| 541 | salt: [salt_length]u8, | 583 | salt: [salt_length]u8, |
| 542 | params: Params, | 584 | params: Params, |
| | 585 | silently_truncate_password: bool, |
| 543 | ) [hash_length]u8 { | 586 | ) [hash_length]u8 { |
| 544 | var dk = bcrypt(password, salt, params); | 587 | var dk = if (silently_truncate_password) bcrypt(password, salt, params) else bcryptWithoutTruncation(password, salt, params); |
| 545 | | 588 | |
| 546 | var salt_str: [salt_str_length]u8 = undefined; | 589 | var salt_str: [salt_str_length]u8 = undefined; |
| 547 | _ = Codec.Encoder.encode(salt_str[0..], salt[0..]); | 590 | _ = Codec.Encoder.encode(salt_str[0..], salt[0..]); |
| ... | @@ -573,15 +616,16 @@ const PhcFormatHasher = struct { | ... | @@ -573,15 +616,16 @@ const PhcFormatHasher = struct { |
| 573 | }; | 616 | }; |
| 574 | | 617 | |
| 575 | /// Return a non-deterministic hash of the password encoded as a PHC-format string | 618 | /// Return a non-deterministic hash of the password encoded as a PHC-format string |
| 576 | pub fn create( | 619 | fn create( |
| 577 | password: []const u8, | 620 | password: []const u8, |
| 578 | params: Params, | 621 | params: Params, |
| | 622 | silently_truncate_password: bool, |
| 579 | buf: []u8, | 623 | buf: []u8, |
| 580 | ) HasherError![]const u8 { | 624 | ) HasherError![]const u8 { |
| 581 | var salt: [salt_length]u8 = undefined; | 625 | var salt: [salt_length]u8 = undefined; |
| 582 | crypto.random.bytes(&salt); | 626 | crypto.random.bytes(&salt); |
| 583 | | 627 | |
| 584 | const hash = bcrypt(password, salt, params); | 628 | const hash = if (silently_truncate_password) bcrypt(password, salt, params) else bcryptWithoutTruncation(password, salt, params); |
| 585 | | 629 | |
| 586 | return phc_format.serialize(HashResult{ | 630 | return phc_format.serialize(HashResult{ |
| 587 | .alg_id = alg_id, | 631 | .alg_id = alg_id, |
| ... | @@ -592,9 +636,10 @@ const PhcFormatHasher = struct { | ... | @@ -592,9 +636,10 @@ const PhcFormatHasher = struct { |
| 592 | } | 636 | } |
| 593 | | 637 | |
| 594 | /// Verify a password against a PHC-format encoded string | 638 | /// Verify a password against a PHC-format encoded string |
| 595 | pub fn verify( | 639 | fn verify( |
| 596 | str: []const u8, | 640 | str: []const u8, |
| 597 | password: []const u8, | 641 | password: []const u8, |
| | 642 | silently_truncate_password: bool, |
| 598 | ) HasherError!void { | 643 | ) HasherError!void { |
| 599 | const hash_result = try phc_format.deserialize(HashResult, str); | 644 | const hash_result = try phc_format.deserialize(HashResult, str); |
| 600 | | 645 | |
| ... | @@ -602,7 +647,8 @@ const PhcFormatHasher = struct { | ... | @@ -602,7 +647,8 @@ const PhcFormatHasher = struct { |
| 602 | if (hash_result.salt.len != salt_length or hash_result.hash.len != dk_length) | 647 | if (hash_result.salt.len != salt_length or hash_result.hash.len != dk_length) |
| 603 | return HasherError.InvalidEncoding; | 648 | return HasherError.InvalidEncoding; |
| 604 | | 649 | |
| 605 | const hash = bcrypt(password, hash_result.salt.buf, .{ .rounds_log = hash_result.r }); | 650 | const params = Params{ .rounds_log = hash_result.r }; |
| | 651 | const hash = if (silently_truncate_password) bcrypt(password, hash_result.salt.buf, params) else bcryptWithoutTruncation(password, hash_result.salt.buf, params); |
| 606 | const expected_hash = hash_result.hash.constSlice(); | 652 | const expected_hash = hash_result.hash.constSlice(); |
| 607 | | 653 | |
| 608 | if (!mem.eql(u8, &hash, expected_hash)) return HasherError.PasswordVerificationFailed; | 654 | if (!mem.eql(u8, &hash, expected_hash)) return HasherError.PasswordVerificationFailed; |
| ... | @@ -612,12 +658,13 @@ const PhcFormatHasher = struct { | ... | @@ -612,12 +658,13 @@ const PhcFormatHasher = struct { |
| 612 | /// Hash and verify passwords using the modular crypt format. | 658 | /// Hash and verify passwords using the modular crypt format. |
| 613 | const CryptFormatHasher = struct { | 659 | const CryptFormatHasher = struct { |
| 614 | /// Length of a string returned by the create() function | 660 | /// Length of a string returned by the create() function |
| 615 | pub const pwhash_str_length: usize = hash_length; | 661 | const pwhash_str_length: usize = hash_length; |
| 616 | | 662 | |
| 617 | /// Return a non-deterministic hash of the password encoded into the modular crypt format | 663 | /// Return a non-deterministic hash of the password encoded into the modular crypt format |
| 618 | pub fn create( | 664 | fn create( |
| 619 | password: []const u8, | 665 | password: []const u8, |
| 620 | params: Params, | 666 | params: Params, |
| | 667 | silently_truncate_password: bool, |
| 621 | buf: []u8, | 668 | buf: []u8, |
| 622 | ) HasherError![]const u8 { | 669 | ) HasherError![]const u8 { |
| 623 | if (buf.len < pwhash_str_length) return HasherError.NoSpaceLeft; | 670 | if (buf.len < pwhash_str_length) return HasherError.NoSpaceLeft; |
| ... | @@ -625,16 +672,17 @@ const CryptFormatHasher = struct { | ... | @@ -625,16 +672,17 @@ const CryptFormatHasher = struct { |
| 625 | var salt: [salt_length]u8 = undefined; | 672 | var salt: [salt_length]u8 = undefined; |
| 626 | crypto.random.bytes(&salt); | 673 | crypto.random.bytes(&salt); |
| 627 | | 674 | |
| 628 | const hash = crypt_format.strHashInternal(password, salt, params); | 675 | const hash = crypt_format.strHashInternal(password, salt, params, silently_truncate_password); |
| 629 | @memcpy(buf[0..hash.len], &hash); | 676 | @memcpy(buf[0..hash.len], &hash); |
| 630 | | 677 | |
| 631 | return buf[0..pwhash_str_length]; | 678 | return buf[0..pwhash_str_length]; |
| 632 | } | 679 | } |
| 633 | | 680 | |
| 634 | /// Verify a password against a string in modular crypt format | 681 | /// Verify a password against a string in modular crypt format |
| 635 | pub fn verify( | 682 | fn verify( |
| 636 | str: []const u8, | 683 | str: []const u8, |
| 637 | password: []const u8, | 684 | password: []const u8, |
| | 685 | silently_truncate_password: bool, |
| 638 | ) HasherError!void { | 686 | ) HasherError!void { |
| 639 | if (str.len != pwhash_str_length or str[3] != '$' or str[6] != '$') | 687 | if (str.len != pwhash_str_length or str[3] != '$' or str[6] != '$') |
| 640 | return HasherError.InvalidEncoding; | 688 | return HasherError.InvalidEncoding; |
| ... | @@ -647,16 +695,22 @@ const CryptFormatHasher = struct { | ... | @@ -647,16 +695,22 @@ const CryptFormatHasher = struct { |
| 647 | var salt: [salt_length]u8 = undefined; | 695 | var salt: [salt_length]u8 = undefined; |
| 648 | crypt_format.Codec.Decoder.decode(salt[0..], salt_str[0..]) catch return HasherError.InvalidEncoding; | 696 | crypt_format.Codec.Decoder.decode(salt[0..], salt_str[0..]) catch return HasherError.InvalidEncoding; |
| 649 | | 697 | |
| 650 | const wanted_s = crypt_format.strHashInternal(password, salt, .{ .rounds_log = rounds_log }); | 698 | const wanted_s = crypt_format.strHashInternal(password, salt, .{ .rounds_log = rounds_log }, silently_truncate_password); |
| 651 | if (!mem.eql(u8, wanted_s[0..], str[0..])) return HasherError.PasswordVerificationFailed; | 699 | if (!mem.eql(u8, wanted_s[0..], str[0..])) return HasherError.PasswordVerificationFailed; |
| 652 | } | 700 | } |
| 653 | }; | 701 | }; |
| 654 | | 702 | |
| 655 | /// Options for hashing a password. | 703 | /// Options for hashing a password. |
| 656 | pub const HashOptions = struct { | 704 | pub const HashOptions = struct { |
| | 705 | /// For `bcrypt`, that can be left to `null`. |
| 657 | allocator: ?mem.Allocator = null, | 706 | allocator: ?mem.Allocator = null, |
| | 707 | /// Internal bcrypt parameters. |
| 658 | params: Params, | 708 | params: Params, |
| | 709 | /// Encoding to use for the output of the hash function. |
| 659 | encoding: pwhash.Encoding, | 710 | encoding: pwhash.Encoding, |
| | 711 | /// Whether to silently truncate the password to 72 bytes, or pre-hash the password when it is longer. |
| | 712 | /// The default is `true`, for compatibility with the original bcrypt implementation. |
| | 713 | silently_truncate_password: bool = true, |
| 660 | }; | 714 | }; |
| 661 | | 715 | |
| 662 | /// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key stretching function. | 716 | /// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key stretching function. |
| ... | @@ -665,34 +719,36 @@ pub const HashOptions = struct { | ... | @@ -665,34 +719,36 @@ pub const HashOptions = struct { |
| 665 | /// The function returns a string that includes all the parameters required for verification. | 719 | /// The function returns a string that includes all the parameters required for verification. |
| 666 | /// | 720 | /// |
| 667 | /// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes. | 721 | /// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes. |
| 668 | /// If this is an issue for your application, hash the password first using a function such as SHA-512, | 722 | /// If this is an issue for your application, set the `silently_truncate_password` option to `false`. |
| 669 | /// and then use the resulting hash as the password parameter for bcrypt. | | |
| 670 | pub fn strHash( | 723 | pub fn strHash( |
| 671 | password: []const u8, | 724 | password: []const u8, |
| 672 | options: HashOptions, | 725 | options: HashOptions, |
| 673 | out: []u8, | 726 | out: []u8, |
| 674 | ) Error![]const u8 { | 727 | ) Error![]const u8 { |
| 675 | switch (options.encoding) { | 728 | switch (options.encoding) { |
| 676 | .phc => return PhcFormatHasher.create(password, options.params, out), | 729 | .phc => return PhcFormatHasher.create(password, options.params, options.silently_truncate_password, out), |
| 677 | .crypt => return CryptFormatHasher.create(password, options.params, out), | 730 | .crypt => return CryptFormatHasher.create(password, options.params, options.silently_truncate_password, out), |
| 678 | } | 731 | } |
| 679 | } | 732 | } |
| 680 | | 733 | |
| 681 | /// Options for hash verification. | 734 | /// Options for hash verification. |
| 682 | pub const VerifyOptions = struct { | 735 | pub const VerifyOptions = struct { |
| | 736 | /// For `bcrypt`, that can be left to `null`. |
| 683 | allocator: ?mem.Allocator = null, | 737 | allocator: ?mem.Allocator = null, |
| | 738 | /// Whether to silently truncate the password to 72 bytes, or pre-hash the password when it is longer. |
| | 739 | silently_truncate_password: bool = false, |
| 684 | }; | 740 | }; |
| 685 | | 741 | |
| 686 | /// Verify that a previously computed hash is valid for a given password. | 742 | /// Verify that a previously computed hash is valid for a given password. |
| 687 | pub fn strVerify( | 743 | pub fn strVerify( |
| 688 | str: []const u8, | 744 | str: []const u8, |
| 689 | password: []const u8, | 745 | password: []const u8, |
| 690 | _: VerifyOptions, | 746 | options: VerifyOptions, |
| 691 | ) Error!void { | 747 | ) Error!void { |
| 692 | if (mem.startsWith(u8, str, crypt_format.prefix)) { | 748 | if (mem.startsWith(u8, str, crypt_format.prefix)) { |
| 693 | return CryptFormatHasher.verify(str, password); | 749 | return CryptFormatHasher.verify(str, password, options.silently_truncate_password); |
| 694 | } else { | 750 | } else { |
| 695 | return PhcFormatHasher.verify(str, password); | 751 | return PhcFormatHasher.verify(str, password, options.silently_truncate_password); |
| 696 | } | 752 | } |
| 697 | } | 753 | } |
| 698 | | 754 | |
| ... | @@ -707,11 +763,12 @@ test "bcrypt codec" { | ... | @@ -707,11 +763,12 @@ test "bcrypt codec" { |
| 707 | } | 763 | } |
| 708 | | 764 | |
| 709 | test "bcrypt crypt format" { | 765 | test "bcrypt crypt format" { |
| 710 | const hash_options = HashOptions{ | 766 | var hash_options = HashOptions{ |
| 711 | .params = .{ .rounds_log = 5 }, | 767 | .params = .{ .rounds_log = 5 }, |
| 712 | .encoding = .crypt, | 768 | .encoding = .crypt, |
| | 769 | .silently_truncate_password = false, |
| 713 | }; | 770 | }; |
| 714 | const verify_options = VerifyOptions{}; | 771 | var verify_options = VerifyOptions{}; |
| 715 | | 772 | |
| 716 | var buf: [hash_length]u8 = undefined; | 773 | var buf: [hash_length]u8 = undefined; |
| 717 | const s = try strHash("password", hash_options, &buf); | 774 | const s = try strHash("password", hash_options, &buf); |
| ... | @@ -724,10 +781,18 @@ test "bcrypt crypt format" { | ... | @@ -724,10 +781,18 @@ test "bcrypt crypt format" { |
| 724 | ); | 781 | ); |
| 725 | | 782 | |
| 726 | var long_buf: [hash_length]u8 = undefined; | 783 | var long_buf: [hash_length]u8 = undefined; |
| 727 | const long_s = try strHash("password" ** 100, hash_options, &long_buf); | 784 | var long_s = try strHash("password" ** 100, hash_options, &long_buf); |
| 728 | | 785 | |
| 729 | try testing.expect(mem.startsWith(u8, long_s, crypt_format.prefix)); | 786 | try testing.expect(mem.startsWith(u8, long_s, crypt_format.prefix)); |
| 730 | try strVerify(long_s, "password" ** 100, verify_options); | 787 | try strVerify(long_s, "password" ** 100, verify_options); |
| | 788 | try testing.expectError( |
| | 789 | error.PasswordVerificationFailed, |
| | 790 | strVerify(long_s, "password" ** 101, verify_options), |
| | 791 | ); |
| | 792 | |
| | 793 | hash_options.silently_truncate_password = true; |
| | 794 | verify_options.silently_truncate_password = true; |
| | 795 | long_s = try strHash("password" ** 100, hash_options, &long_buf); |
| 731 | try strVerify(long_s, "password" ** 101, verify_options); | 796 | try strVerify(long_s, "password" ** 101, verify_options); |
| 732 | | 797 | |
| 733 | try strVerify( | 798 | try strVerify( |
| ... | @@ -738,11 +803,12 @@ test "bcrypt crypt format" { | ... | @@ -738,11 +803,12 @@ test "bcrypt crypt format" { |
| 738 | } | 803 | } |
| 739 | | 804 | |
| 740 | test "bcrypt phc format" { | 805 | test "bcrypt phc format" { |
| 741 | const hash_options = HashOptions{ | 806 | var hash_options = HashOptions{ |
| 742 | .params = .{ .rounds_log = 5 }, | 807 | .params = .{ .rounds_log = 5 }, |
| 743 | .encoding = .phc, | 808 | .encoding = .phc, |
| | 809 | .silently_truncate_password = false, |
| 744 | }; | 810 | }; |
| 745 | const verify_options = VerifyOptions{}; | 811 | var verify_options = VerifyOptions{}; |
| 746 | const prefix = "$bcrypt$"; | 812 | const prefix = "$bcrypt$"; |
| 747 | | 813 | |
| 748 | var buf: [hash_length * 2]u8 = undefined; | 814 | var buf: [hash_length * 2]u8 = undefined; |
| ... | @@ -756,10 +822,18 @@ test "bcrypt phc format" { | ... | @@ -756,10 +822,18 @@ test "bcrypt phc format" { |
| 756 | ); | 822 | ); |
| 757 | | 823 | |
| 758 | var long_buf: [hash_length * 2]u8 = undefined; | 824 | var long_buf: [hash_length * 2]u8 = undefined; |
| 759 | const long_s = try strHash("password" ** 100, hash_options, &long_buf); | 825 | var long_s = try strHash("password" ** 100, hash_options, &long_buf); |
| 760 | | 826 | |
| 761 | try testing.expect(mem.startsWith(u8, long_s, prefix)); | 827 | try testing.expect(mem.startsWith(u8, long_s, prefix)); |
| 762 | try strVerify(long_s, "password" ** 100, verify_options); | 828 | try strVerify(long_s, "password" ** 100, verify_options); |
| | 829 | try testing.expectError( |
| | 830 | error.PasswordVerificationFailed, |
| | 831 | strVerify(long_s, "password" ** 101, verify_options), |
| | 832 | ); |
| | 833 | |
| | 834 | hash_options.silently_truncate_password = true; |
| | 835 | verify_options.silently_truncate_password = true; |
| | 836 | long_s = try strHash("password" ** 100, hash_options, &long_buf); |
| 763 | try strVerify(long_s, "password" ** 101, verify_options); | 837 | try strVerify(long_s, "password" ** 101, verify_options); |
| 764 | | 838 | |
| 765 | try strVerify( | 839 | try strVerify( |