Skip to content

Commit aeade42

Browse files
authored
Merge branch 'master' into import-zon-anon-type
2 parents 3987bb4 + 8d824df commit aeade42

File tree

6 files changed

+1580
-49
lines changed

6 files changed

+1580
-49
lines changed

lib/std/crypto/bcrypt.zig

Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -412,21 +412,19 @@ pub const Params = struct {
412412
/// log2 of the number of rounds
413413
rounds_log: u6,
414414

415+
/// As originally defined, bcrypt silently truncates passwords to 72 bytes.
416+
/// In order to overcome this limitation, if `silently_truncate_password` is set to `false`,
417+
/// long passwords will be automatically pre-hashed using HMAC-SHA512 before being passed to bcrypt.
418+
/// Only set `silently_truncate_password` to `true` for compatibility with traditional bcrypt implementations,
419+
/// or if you want to handle the truncation yourself.
420+
silently_truncate_password: bool,
421+
415422
/// Minimum recommended parameters according to the
416423
/// [OWASP cheat sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html).
417-
pub const owasp = Self{ .rounds_log = 10 };
424+
pub const owasp = Self{ .rounds_log = 10, .silently_truncate_password = false };
418425
};
419426

420-
/// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key stretching function.
421-
/// bcrypt is a computationally expensive and cache-hard function, explicitly designed to slow down exhaustive searches.
422-
///
423-
/// The function returns the hash as a `dk_length` byte array, that doesn't include anything besides the hash output.
424-
///
425-
/// For a generic key-derivation function, use `bcrypt.pbkdf()` instead.
426-
///
427-
/// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes.
428-
/// If this is an issue for your application, use `bcryptWithoutTruncation` instead.
429-
pub fn bcrypt(
427+
fn bcryptWithTruncation(
430428
password: []const u8,
431429
salt: [salt_length]u8,
432430
params: Params,
@@ -465,17 +463,15 @@ pub fn bcrypt(
465463
///
466464
/// The function returns the hash as a `dk_length` byte array, that doesn't include anything besides the hash output.
467465
///
468-
/// For a generic key-derivation function, use `bcrypt.pbkdf()` instead.
469-
///
470-
/// This function is identical to `bcrypt`, except that it doesn't silently truncate passwords.
471-
/// Instead, passwords longer than 72 bytes are pre-hashed using HMAC-SHA512 before being passed to bcrypt.
472-
pub fn bcryptWithoutTruncation(
466+
/// This function was designed for password storage, not for key derivation.
467+
/// For key derivation, use `bcrypt.pbkdf()` or `bcrypt.opensshKdf()` instead.
468+
pub fn bcrypt(
473469
password: []const u8,
474470
salt: [salt_length]u8,
475471
params: Params,
476472
) [dk_length]u8 {
477-
if (password.len <= 72) {
478-
return bcrypt(password, salt, params);
473+
if (password.len <= 72 or params.silently_truncate_password) {
474+
return bcryptWithTruncation(password, salt, params);
479475
}
480476

481477
var pre_hash: [HmacSha512.mac_length]u8 = undefined;
@@ -485,7 +481,7 @@ pub fn bcryptWithoutTruncation(
485481
var pre_hash_b64: [Encoder.calcSize(pre_hash.len)]u8 = undefined;
486482
_ = Encoder.encode(&pre_hash_b64, &pre_hash);
487483

488-
return bcrypt(&pre_hash_b64, salt, params);
484+
return bcryptWithTruncation(&pre_hash_b64, salt, params);
489485
}
490486

491487
const pbkdf_prf = struct {
@@ -629,9 +625,8 @@ const crypt_format = struct {
629625
password: []const u8,
630626
salt: [salt_length]u8,
631627
params: Params,
632-
silently_truncate_password: bool,
633628
) [hash_length]u8 {
634-
var dk = if (silently_truncate_password) bcrypt(password, salt, params) else bcryptWithoutTruncation(password, salt, params);
629+
var dk = bcrypt(password, salt, params);
635630

636631
var salt_str: [salt_str_length]u8 = undefined;
637632
_ = Codec.Encoder.encode(salt_str[0..], salt[0..]);
@@ -666,13 +661,12 @@ const PhcFormatHasher = struct {
666661
fn create(
667662
password: []const u8,
668663
params: Params,
669-
silently_truncate_password: bool,
670664
buf: []u8,
671665
) HasherError![]const u8 {
672666
var salt: [salt_length]u8 = undefined;
673667
crypto.random.bytes(&salt);
674668

675-
const hash = if (silently_truncate_password) bcrypt(password, salt, params) else bcryptWithoutTruncation(password, salt, params);
669+
const hash = bcrypt(password, salt, params);
676670

677671
return phc_format.serialize(HashResult{
678672
.alg_id = alg_id,
@@ -694,8 +688,11 @@ const PhcFormatHasher = struct {
694688
if (hash_result.salt.len != salt_length or hash_result.hash.len != dk_length)
695689
return HasherError.InvalidEncoding;
696690

697-
const params = Params{ .rounds_log = hash_result.r };
698-
const hash = if (silently_truncate_password) bcrypt(password, hash_result.salt.buf, params) else bcryptWithoutTruncation(password, hash_result.salt.buf, params);
691+
const params = Params{
692+
.rounds_log = hash_result.r,
693+
.silently_truncate_password = silently_truncate_password,
694+
};
695+
const hash = bcrypt(password, hash_result.salt.buf, params);
699696
const expected_hash = hash_result.hash.constSlice();
700697

701698
if (!mem.eql(u8, &hash, expected_hash)) return HasherError.PasswordVerificationFailed;
@@ -711,15 +708,14 @@ const CryptFormatHasher = struct {
711708
fn create(
712709
password: []const u8,
713710
params: Params,
714-
silently_truncate_password: bool,
715711
buf: []u8,
716712
) HasherError![]const u8 {
717713
if (buf.len < pwhash_str_length) return HasherError.NoSpaceLeft;
718714

719715
var salt: [salt_length]u8 = undefined;
720716
crypto.random.bytes(&salt);
721717

722-
const hash = crypt_format.strHashInternal(password, salt, params, silently_truncate_password);
718+
const hash = crypt_format.strHashInternal(password, salt, params);
723719
@memcpy(buf[0..hash.len], &hash);
724720

725721
return buf[0..pwhash_str_length];
@@ -742,7 +738,10 @@ const CryptFormatHasher = struct {
742738
var salt: [salt_length]u8 = undefined;
743739
crypt_format.Codec.Decoder.decode(salt[0..], salt_str[0..]) catch return HasherError.InvalidEncoding;
744740

745-
const wanted_s = crypt_format.strHashInternal(password, salt, .{ .rounds_log = rounds_log }, silently_truncate_password);
741+
const wanted_s = crypt_format.strHashInternal(password, salt, .{
742+
.rounds_log = rounds_log,
743+
.silently_truncate_password = silently_truncate_password,
744+
});
746745
if (!mem.eql(u8, wanted_s[0..], str[0..])) return HasherError.PasswordVerificationFailed;
747746
}
748747
};
@@ -755,9 +754,6 @@ pub const HashOptions = struct {
755754
params: Params,
756755
/// Encoding to use for the output of the hash function.
757756
encoding: pwhash.Encoding,
758-
/// Whether to silently truncate the password to 72 bytes, or pre-hash the password when it is longer.
759-
/// The default is `true`, for compatibility with the original bcrypt implementation.
760-
silently_truncate_password: bool = true,
761757
};
762758

763759
/// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key stretching function.
@@ -773,8 +769,8 @@ pub fn strHash(
773769
out: []u8,
774770
) Error![]const u8 {
775771
switch (options.encoding) {
776-
.phc => return PhcFormatHasher.create(password, options.params, options.silently_truncate_password, out),
777-
.crypt => return CryptFormatHasher.create(password, options.params, options.silently_truncate_password, out),
772+
.phc => return PhcFormatHasher.create(password, options.params, out),
773+
.crypt => return CryptFormatHasher.create(password, options.params, out),
778774
}
779775
}
780776

@@ -783,7 +779,7 @@ pub const VerifyOptions = struct {
783779
/// For `bcrypt`, that can be left to `null`.
784780
allocator: ?mem.Allocator = null,
785781
/// Whether to silently truncate the password to 72 bytes, or pre-hash the password when it is longer.
786-
silently_truncate_password: bool = false,
782+
silently_truncate_password: bool,
787783
};
788784

789785
/// Verify that a previously computed hash is valid for a given password.
@@ -811,11 +807,10 @@ test "bcrypt codec" {
811807

812808
test "bcrypt crypt format" {
813809
var hash_options = HashOptions{
814-
.params = .{ .rounds_log = 5 },
810+
.params = .{ .rounds_log = 5, .silently_truncate_password = false },
815811
.encoding = .crypt,
816-
.silently_truncate_password = false,
817812
};
818-
var verify_options = VerifyOptions{};
813+
var verify_options = VerifyOptions{ .silently_truncate_password = false };
819814

820815
var buf: [hash_length]u8 = undefined;
821816
const s = try strHash("password", hash_options, &buf);
@@ -837,7 +832,7 @@ test "bcrypt crypt format" {
837832
strVerify(long_s, "password" ** 101, verify_options),
838833
);
839834

840-
hash_options.silently_truncate_password = true;
835+
hash_options.params.silently_truncate_password = true;
841836
verify_options.silently_truncate_password = true;
842837
long_s = try strHash("password" ** 100, hash_options, &long_buf);
843838
try strVerify(long_s, "password" ** 101, verify_options);
@@ -851,11 +846,10 @@ test "bcrypt crypt format" {
851846

852847
test "bcrypt phc format" {
853848
var hash_options = HashOptions{
854-
.params = .{ .rounds_log = 5 },
849+
.params = .{ .rounds_log = 5, .silently_truncate_password = false },
855850
.encoding = .phc,
856-
.silently_truncate_password = false,
857851
};
858-
var verify_options = VerifyOptions{};
852+
var verify_options = VerifyOptions{ .silently_truncate_password = false };
859853
const prefix = "$bcrypt$";
860854

861855
var buf: [hash_length * 2]u8 = undefined;
@@ -878,7 +872,7 @@ test "bcrypt phc format" {
878872
strVerify(long_s, "password" ** 101, verify_options),
879873
);
880874

881-
hash_options.silently_truncate_password = true;
875+
hash_options.params.silently_truncate_password = true;
882876
verify_options.silently_truncate_password = true;
883877
long_s = try strHash("password" ** 100, hash_options, &long_buf);
884878
try strVerify(long_s, "password" ** 101, verify_options);

lib/std/crypto/benchmark.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ const CryptoPwhash = struct {
391391
params: *const anyopaque,
392392
name: []const u8,
393393
};
394-
const bcrypt_params = crypto.pwhash.bcrypt.Params{ .rounds_log = 8 };
394+
const bcrypt_params = crypto.pwhash.bcrypt.Params{ .rounds_log = 8, .silently_truncate_password = true };
395395
const pwhashes = [_]CryptoPwhash{
396396
.{
397397
.ty = crypto.pwhash.bcrypt,

lib/std/math/big/int.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2544,8 +2544,7 @@ pub const Const = struct {
25442544
const bits_per_limb = @bitSizeOf(Limb);
25452545
while (i != 0) {
25462546
i -= 1;
2547-
const limb = a.limbs[i];
2548-
const this_limb_lz = @clz(limb);
2547+
const this_limb_lz = @clz(a.limbs[i]);
25492548
total_limb_lz += this_limb_lz;
25502549
if (this_limb_lz != bits_per_limb) break;
25512550
}
@@ -2557,6 +2556,7 @@ pub const Const = struct {
25572556
pub fn ctz(a: Const, bits: Limb) Limb {
25582557
// Limbs are stored in little-endian order. Converting a negative number to twos-complement
25592558
// flips all bits above the lowest set bit, which does not affect the trailing zero count.
2559+
if (a.eqlZero()) return bits;
25602560
var result: Limb = 0;
25612561
for (a.limbs) |limb| {
25622562
const limb_tz = @ctz(limb);

0 commit comments

Comments
 (0)