@@ -752,6 +752,13 @@ pub const HashOptions = struct {
752
752
params : Params ,
753
753
/// Encoding to use for the output of the hash function.
754
754
encoding : pwhash.Encoding ,
755
+ /// The maximum length in bytes for a hashed password. If not
756
+ /// set by the developer, this varies based on encoding.
757
+ /// `crypt` encoding uses 60 bytes, and `phc` encoding uses
758
+ /// 120 bytes.
759
+ ///
760
+ /// The returned hash may be smaller than the maximum length.
761
+ strhash_max_bytes : ? usize = null ,
755
762
};
756
763
757
764
/// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key stretching function.
@@ -762,14 +769,21 @@ pub const HashOptions = struct {
762
769
/// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes.
763
770
/// If this is an issue for your application, set the `silently_truncate_password` option to `false`.
764
771
pub fn strHash (
772
+ allocator : mem.Allocator ,
765
773
password : []const u8 ,
766
- options : HashOptions ,
767
- out : []u8 ,
768
- ) Error ! []const u8 {
769
- switch (options .encoding ) {
770
- .phc = > return PhcFormatHasher .create (password , options .params , out ),
771
- .crypt = > return CryptFormatHasher .create (password , options .params , out ),
772
- }
774
+ comptime options : HashOptions ,
775
+ ) Error ! []u8 {
776
+ const buf_len = comptime if (options .strhash_max_bytes ) | len | len else switch (options .encoding ) {
777
+ .crypt = > hash_length ,
778
+ .phc = > hash_length * 2 ,
779
+ };
780
+ var buf : [buf_len ]u8 = undefined ;
781
+
782
+ const out = switch (options .encoding ) {
783
+ .phc = > try PhcFormatHasher .create (password , options .params , & buf ),
784
+ .crypt = > try CryptFormatHasher .create (password , options .params , & buf ),
785
+ };
786
+ return allocator .dupe (u8 , out );
773
787
}
774
788
775
789
/// Options for hash verification.
@@ -802,14 +816,15 @@ test "bcrypt codec" {
802
816
}
803
817
804
818
test "bcrypt crypt format" {
805
- var hash_options = HashOptions {
819
+ const allocator = std .testing .allocator ;
820
+ const hash_options = HashOptions {
806
821
.params = .{ .rounds_log = 5 , .silently_truncate_password = false },
807
822
.encoding = .crypt ,
808
823
};
809
- var verify_options = VerifyOptions { .silently_truncate_password = false };
824
+ const verify_options = VerifyOptions { .silently_truncate_password = false };
810
825
811
- var buf : [ hash_length ] u8 = undefined ;
812
- const s = try strHash ( "password" , hash_options , & buf );
826
+ const s = try strHash ( allocator , "password" , hash_options ) ;
827
+ defer allocator . free ( s );
813
828
814
829
try testing .expect (mem .startsWith (u8 , s , crypt_format .prefix ));
815
830
try strVerify (s , "password" , verify_options );
@@ -818,8 +833,7 @@ test "bcrypt crypt format" {
818
833
strVerify (s , "invalid password" , verify_options ),
819
834
);
820
835
821
- var long_buf : [hash_length ]u8 = undefined ;
822
- var long_s = try strHash ("password" ** 100 , hash_options , & long_buf );
836
+ var long_s = try strHash (allocator , "password" ** 100 , hash_options );
823
837
824
838
try testing .expect (mem .startsWith (u8 , long_s , crypt_format .prefix ));
825
839
try strVerify (long_s , "password" ** 100 , verify_options );
@@ -828,28 +842,33 @@ test "bcrypt crypt format" {
828
842
strVerify (long_s , "password" ** 101 , verify_options ),
829
843
);
830
844
831
- hash_options .params .silently_truncate_password = true ;
832
- verify_options .silently_truncate_password = true ;
833
- long_s = try strHash ("password" ** 100 , hash_options , & long_buf );
834
- try strVerify (long_s , "password" ** 101 , verify_options );
845
+ allocator .free (long_s );
846
+
847
+ long_s = try strHash (allocator , "password" ** 100 , .{
848
+ .params = .{ .rounds_log = 5 , .silently_truncate_password = true },
849
+ .encoding = .crypt ,
850
+ });
851
+ defer allocator .free (long_s );
852
+ try strVerify (long_s , "password" ** 101 , .{ .silently_truncate_password = true });
835
853
836
854
try strVerify (
837
855
"$2b$08$WUQKyBCaKpziCwUXHiMVvu40dYVjkTxtWJlftl0PpjY2BxWSvFIEe" ,
838
856
"The devil himself" ,
839
- verify_options ,
857
+ .{ . silently_truncate_password = true } ,
840
858
);
841
859
}
842
860
843
861
test "bcrypt phc format" {
844
- var hash_options = HashOptions {
862
+ const allocator = std .testing .allocator ;
863
+ const hash_options = HashOptions {
845
864
.params = .{ .rounds_log = 5 , .silently_truncate_password = false },
846
865
.encoding = .phc ,
847
866
};
848
- var verify_options = VerifyOptions { .silently_truncate_password = false };
867
+ const verify_options = VerifyOptions { .silently_truncate_password = false };
849
868
const prefix = "$bcrypt$" ;
850
869
851
- var buf : [ hash_length * 2 ] u8 = undefined ;
852
- const s = try strHash ( "password" , hash_options , & buf );
870
+ const s = try strHash ( allocator , "password" , hash_options ) ;
871
+ defer allocator . free ( s );
853
872
854
873
try testing .expect (mem .startsWith (u8 , s , prefix ));
855
874
try strVerify (s , "password" , verify_options );
@@ -858,8 +877,7 @@ test "bcrypt phc format" {
858
877
strVerify (s , "invalid password" , verify_options ),
859
878
);
860
879
861
- var long_buf : [hash_length * 2 ]u8 = undefined ;
862
- var long_s = try strHash ("password" ** 100 , hash_options , & long_buf );
880
+ var long_s = try strHash (allocator , "password" ** 100 , hash_options );
863
881
864
882
try testing .expect (mem .startsWith (u8 , long_s , prefix ));
865
883
try strVerify (long_s , "password" ** 100 , verify_options );
@@ -868,10 +886,14 @@ test "bcrypt phc format" {
868
886
strVerify (long_s , "password" ** 101 , verify_options ),
869
887
);
870
888
871
- hash_options .params .silently_truncate_password = true ;
872
- verify_options .silently_truncate_password = true ;
873
- long_s = try strHash ("password" ** 100 , hash_options , & long_buf );
874
- try strVerify (long_s , "password" ** 101 , verify_options );
889
+ allocator .free (long_s );
890
+
891
+ long_s = try strHash (allocator , "password" ** 100 , .{
892
+ .params = .{ .rounds_log = 5 , .silently_truncate_password = true },
893
+ .encoding = .crypt ,
894
+ });
895
+ defer allocator .free (long_s );
896
+ try strVerify (long_s , "password" ** 101 , .{ .silently_truncate_password = true });
875
897
876
898
try strVerify (
877
899
"$bcrypt$r=5$2NopntlgE2lX3cTwr4qz8A$r3T7iKYQNnY4hAhGjk9RmuyvgrYJZwc" ,
0 commit comments