mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-03-08 02:44:43 +01:00
Merge pull request 'crypto.scrypt: accept an std.Io parameter instead of direct entropy' (#30738) from jedisct1/zig:scryptfixes into master
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30738 Reviewed-by: Andrew Kelley <andrew@ziglang.org>
This commit is contained in:
commit
721bdb6256
2 changed files with 149 additions and 49 deletions
|
|
@ -662,15 +662,27 @@ const PhcFormatHasher = struct {
|
|||
password: []const u8,
|
||||
params: Params,
|
||||
buf: []u8,
|
||||
/// Filled with cryptographically secure entropy.
|
||||
salt: *const [salt_length]u8,
|
||||
io: std.Io,
|
||||
) HasherError![]const u8 {
|
||||
const hash = bcrypt(password, salt, params);
|
||||
var salt: [salt_length]u8 = undefined;
|
||||
io.random(&salt);
|
||||
return createWithSalt(password, params, buf, salt);
|
||||
}
|
||||
|
||||
/// Return a deterministic hash of the password encoded as a PHC-format string.
|
||||
/// Uses the provided salt instead of generating one randomly.
|
||||
fn createWithSalt(
|
||||
password: []const u8,
|
||||
params: Params,
|
||||
buf: []u8,
|
||||
salt: [salt_length]u8,
|
||||
) HasherError![]const u8 {
|
||||
const hash = bcrypt(password, &salt, params);
|
||||
|
||||
return phc_format.serialize(HashResult{
|
||||
.alg_id = alg_id,
|
||||
.r = params.rounds_log,
|
||||
.salt = try BinValue(salt_length).fromSlice(salt),
|
||||
.salt = try BinValue(salt_length).fromSlice(&salt),
|
||||
.hash = try BinValue(dk_length).fromSlice(&hash),
|
||||
}, buf);
|
||||
}
|
||||
|
|
@ -708,7 +720,19 @@ const CryptFormatHasher = struct {
|
|||
password: []const u8,
|
||||
params: Params,
|
||||
buf: []u8,
|
||||
/// Filled with cryptographically secure entropy.
|
||||
io: std.Io,
|
||||
) HasherError![]const u8 {
|
||||
var salt: [salt_length]u8 = undefined;
|
||||
io.random(&salt);
|
||||
return createWithSalt(password, params, buf, &salt);
|
||||
}
|
||||
|
||||
/// Return a deterministic hash of the password encoded into the modular crypt format.
|
||||
/// Uses the provided salt instead of generating one randomly.
|
||||
fn createWithSalt(
|
||||
password: []const u8,
|
||||
params: Params,
|
||||
buf: []u8,
|
||||
salt: *const [salt_length]u8,
|
||||
) HasherError![]const u8 {
|
||||
if (buf.len < pwhash_str_length) return HasherError.NoSpaceLeft;
|
||||
|
|
@ -770,12 +794,26 @@ pub fn strHash(
|
|||
password: []const u8,
|
||||
options: HashOptions,
|
||||
out: []u8,
|
||||
/// Filled with cryptographically secure entropy.
|
||||
salt: *const [salt_length]u8,
|
||||
io: std.Io,
|
||||
) Error![]const u8 {
|
||||
switch (options.encoding) {
|
||||
.phc => return PhcFormatHasher.create(password, options.params, out, salt),
|
||||
.crypt => return CryptFormatHasher.create(password, options.params, out, salt),
|
||||
.phc => return PhcFormatHasher.create(password, options.params, out, io),
|
||||
.crypt => return CryptFormatHasher.create(password, options.params, out, io),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute a deterministic hash of a password using the bcrypt key derivation function.
|
||||
/// The function returns a string that includes all the parameters required for verification.
|
||||
/// Uses the provided salt instead of generating one randomly.
|
||||
pub fn strHashWithSalt(
|
||||
password: []const u8,
|
||||
options: HashOptions,
|
||||
out: []u8,
|
||||
salt: [salt_length]u8,
|
||||
) Error![]const u8 {
|
||||
switch (options.encoding) {
|
||||
.phc => return PhcFormatHasher.createWithSalt(password, options.params, out, salt),
|
||||
.crypt => return CryptFormatHasher.createWithSalt(password, options.params, out, &salt),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -821,11 +859,7 @@ test "bcrypt crypt format" {
|
|||
var verify_options: VerifyOptions = .{ .silently_truncate_password = false };
|
||||
|
||||
var buf: [hash_length]u8 = undefined;
|
||||
const s = s: {
|
||||
var salt: [salt_length]u8 = undefined;
|
||||
io.random(&salt);
|
||||
break :s try strHash("password", hash_options, &buf, &salt);
|
||||
};
|
||||
const s = try strHash("password", hash_options, &buf, io);
|
||||
|
||||
try testing.expect(mem.startsWith(u8, s, crypt_format.prefix));
|
||||
try strVerify(s, "password", verify_options);
|
||||
|
|
@ -835,11 +869,7 @@ test "bcrypt crypt format" {
|
|||
);
|
||||
|
||||
var long_buf: [hash_length]u8 = undefined;
|
||||
var long_s = s: {
|
||||
var salt: [salt_length]u8 = undefined;
|
||||
io.random(&salt);
|
||||
break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
|
||||
};
|
||||
var long_s = try strHash("password" ** 100, hash_options, &long_buf, io);
|
||||
|
||||
try testing.expect(mem.startsWith(u8, long_s, crypt_format.prefix));
|
||||
try strVerify(long_s, "password" ** 100, verify_options);
|
||||
|
|
@ -850,11 +880,7 @@ test "bcrypt crypt format" {
|
|||
|
||||
hash_options.params.silently_truncate_password = true;
|
||||
verify_options.silently_truncate_password = true;
|
||||
long_s = s: {
|
||||
var salt: [salt_length]u8 = undefined;
|
||||
io.random(&salt);
|
||||
break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
|
||||
};
|
||||
long_s = try strHash("password" ** 100, hash_options, &long_buf, io);
|
||||
try strVerify(long_s, "password" ** 101, verify_options);
|
||||
|
||||
try strVerify(
|
||||
|
|
@ -874,11 +900,7 @@ test "bcrypt phc format" {
|
|||
const prefix = "$bcrypt$";
|
||||
|
||||
var buf: [hash_length * 2]u8 = undefined;
|
||||
const s = s: {
|
||||
var salt: [salt_length]u8 = undefined;
|
||||
io.random(&salt);
|
||||
break :s try strHash("password", hash_options, &buf, &salt);
|
||||
};
|
||||
const s = try strHash("password", hash_options, &buf, io);
|
||||
|
||||
try testing.expect(mem.startsWith(u8, s, prefix));
|
||||
try strVerify(s, "password", verify_options);
|
||||
|
|
@ -888,11 +910,7 @@ test "bcrypt phc format" {
|
|||
);
|
||||
|
||||
var long_buf: [hash_length * 2]u8 = undefined;
|
||||
var long_s = s: {
|
||||
var salt: [salt_length]u8 = undefined;
|
||||
io.random(&salt);
|
||||
break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
|
||||
};
|
||||
var long_s = try strHash("password" ** 100, hash_options, &long_buf, io);
|
||||
|
||||
try testing.expect(mem.startsWith(u8, long_s, prefix));
|
||||
try strVerify(long_s, "password" ** 100, verify_options);
|
||||
|
|
@ -903,11 +921,7 @@ test "bcrypt phc format" {
|
|||
|
||||
hash_options.params.silently_truncate_password = true;
|
||||
verify_options.silently_truncate_password = true;
|
||||
long_s = s: {
|
||||
var salt: [salt_length]u8 = undefined;
|
||||
io.random(&salt);
|
||||
break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
|
||||
};
|
||||
long_s = try strHash("password" ** 100, hash_options, &long_buf, io);
|
||||
try strVerify(long_s, "password" ** 101, verify_options);
|
||||
|
||||
try strVerify(
|
||||
|
|
@ -917,6 +931,25 @@ test "bcrypt phc format" {
|
|||
);
|
||||
}
|
||||
|
||||
test "strHashWithSalt deterministic" {
|
||||
const password = "testpass";
|
||||
const salt: [salt_length]u8 = "0123456789abcdef".*;
|
||||
const params: Params = .{ .rounds_log = 5, .silently_truncate_password = false };
|
||||
|
||||
var buf1: [hash_length * 2]u8 = undefined;
|
||||
var buf2: [hash_length * 2]u8 = undefined;
|
||||
|
||||
const str1 = try strHashWithSalt(password, .{ .params = params, .encoding = .phc }, &buf1, salt);
|
||||
const str2 = try strHashWithSalt(password, .{ .params = params, .encoding = .phc }, &buf2, salt);
|
||||
try testing.expectEqualStrings(str1, str2);
|
||||
try strVerify(str1, password, .{ .silently_truncate_password = false });
|
||||
|
||||
const str3 = try strHashWithSalt(password, .{ .params = params, .encoding = .crypt }, &buf1, salt);
|
||||
const str4 = try strHashWithSalt(password, .{ .params = params, .encoding = .crypt }, &buf2, salt);
|
||||
try testing.expectEqualStrings(str3, str4);
|
||||
try strVerify(str3, password, .{ .silently_truncate_password = false });
|
||||
}
|
||||
|
||||
test "openssh kdf" {
|
||||
var key: [100]u8 = undefined;
|
||||
const pass = "password";
|
||||
|
|
|
|||
|
|
@ -417,18 +417,31 @@ const PhcFormatHasher = struct {
|
|||
password: []const u8,
|
||||
params: Params,
|
||||
buf: []u8,
|
||||
/// Filled with cryptographically secure entropy.
|
||||
salt: []const u8,
|
||||
io: std.Io,
|
||||
) HasherError![]const u8 {
|
||||
var salt: [default_salt_len]u8 = undefined;
|
||||
io.random(&salt);
|
||||
return createWithSalt(allocator, password, params, buf, &salt);
|
||||
}
|
||||
|
||||
/// Return a deterministic hash of the password encoded as a PHC-format string.
|
||||
/// Uses the provided salt instead of generating one randomly.
|
||||
pub fn createWithSalt(
|
||||
allocator: mem.Allocator,
|
||||
password: []const u8,
|
||||
params: Params,
|
||||
buf: []u8,
|
||||
salt: *const [default_salt_len]u8,
|
||||
) HasherError![]const u8 {
|
||||
var hash: [default_hash_len]u8 = undefined;
|
||||
try kdf(allocator, &hash, password, &salt, params);
|
||||
try kdf(allocator, &hash, password, salt, params);
|
||||
|
||||
return phc_format.serialize(HashResult{
|
||||
.alg_id = alg_id,
|
||||
.ln = params.ln,
|
||||
.r = params.r,
|
||||
.p = params.p,
|
||||
.salt = try BinValue(max_salt_len).fromSlice(&salt),
|
||||
.salt = try BinValue(max_salt_len).fromSlice(salt),
|
||||
.hash = try BinValue(max_hash_len).fromSlice(&hash),
|
||||
}, buf);
|
||||
}
|
||||
|
|
@ -465,10 +478,23 @@ const CryptFormatHasher = struct {
|
|||
password: []const u8,
|
||||
params: Params,
|
||||
buf: []u8,
|
||||
/// Filled with cryptographically secure entropy.
|
||||
salt_bin: []const u8,
|
||||
io: std.Io,
|
||||
) HasherError![]const u8 {
|
||||
const salt = crypt_format.saltFromBin(salt_bin.len, salt_bin);
|
||||
var salt_bin: [default_salt_len]u8 = undefined;
|
||||
io.random(&salt_bin);
|
||||
return createWithSalt(allocator, password, params, buf, &salt_bin);
|
||||
}
|
||||
|
||||
/// Return a deterministic hash of the password encoded into the modular crypt format.
|
||||
/// Uses the provided salt instead of generating one randomly.
|
||||
pub fn createWithSalt(
|
||||
allocator: mem.Allocator,
|
||||
password: []const u8,
|
||||
params: Params,
|
||||
buf: []u8,
|
||||
salt_bin: *const [default_salt_len]u8,
|
||||
) HasherError![]const u8 {
|
||||
const salt = crypt_format.saltFromBin(salt_bin.len, salt_bin.*);
|
||||
|
||||
var hash: [default_hash_len]u8 = undefined;
|
||||
try kdf(allocator, &hash, password, &salt, params);
|
||||
|
|
@ -514,11 +540,28 @@ pub fn strHash(
|
|||
password: []const u8,
|
||||
options: HashOptions,
|
||||
out: []u8,
|
||||
io: std.Io,
|
||||
) Error![]const u8 {
|
||||
const allocator = options.allocator orelse return Error.AllocatorRequired;
|
||||
switch (options.encoding) {
|
||||
.phc => return PhcFormatHasher.create(allocator, password, options.params, out),
|
||||
.crypt => return CryptFormatHasher.create(allocator, password, options.params, out),
|
||||
.phc => return PhcFormatHasher.create(allocator, password, options.params, out, io),
|
||||
.crypt => return CryptFormatHasher.create(allocator, password, options.params, out, io),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute a deterministic hash of a password using the scrypt key derivation function.
|
||||
/// The function returns a string that includes all the parameters required for verification.
|
||||
/// Uses the provided salt instead of generating one randomly.
|
||||
pub fn strHashWithSalt(
|
||||
password: []const u8,
|
||||
options: HashOptions,
|
||||
out: []u8,
|
||||
salt: *const [default_salt_len]u8,
|
||||
) Error![]const u8 {
|
||||
const allocator = options.allocator orelse return Error.AllocatorRequired;
|
||||
switch (options.encoding) {
|
||||
.phc => return PhcFormatHasher.createWithSalt(allocator, password, options.params, out, salt),
|
||||
.crypt => return CryptFormatHasher.createWithSalt(allocator, password, options.params, out, salt),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -630,6 +673,7 @@ test "password hashing (crypt format)" {
|
|||
if (!run_long_tests) return error.SkipZigTest;
|
||||
|
||||
const alloc = std.testing.allocator;
|
||||
const io = std.testing.io;
|
||||
|
||||
const str = "$7$A6....1....TrXs5Zk6s8sWHpQgWDIXTR8kUU3s6Jc3s.DtdS8M2i4$a4ik5hGDN7foMuHOW.cp.CtX01UyCeO0.JAG.AHPpx5";
|
||||
const password = "Y0!?iQa9M%5ekffW(`";
|
||||
|
|
@ -637,7 +681,7 @@ test "password hashing (crypt format)" {
|
|||
|
||||
const params = Params.interactive;
|
||||
var buf: [CryptFormatHasher.pwhash_str_length]u8 = undefined;
|
||||
const str2 = try CryptFormatHasher.create(alloc, password, params, &buf);
|
||||
const str2 = try CryptFormatHasher.create(alloc, password, params, &buf, io);
|
||||
try CryptFormatHasher.verify(alloc, str2, password);
|
||||
}
|
||||
|
||||
|
|
@ -645,6 +689,7 @@ test "strHash and strVerify" {
|
|||
if (!run_long_tests) return error.SkipZigTest;
|
||||
|
||||
const alloc = std.testing.allocator;
|
||||
const io = std.testing.io;
|
||||
|
||||
const password = "testpass";
|
||||
const params = Params.interactive;
|
||||
|
|
@ -656,6 +701,7 @@ test "strHash and strVerify" {
|
|||
password,
|
||||
.{ .allocator = alloc, .params = params, .encoding = .crypt },
|
||||
&buf,
|
||||
io,
|
||||
);
|
||||
try strVerify(str, password, verify_options);
|
||||
}
|
||||
|
|
@ -664,6 +710,7 @@ test "strHash and strVerify" {
|
|||
password,
|
||||
.{ .allocator = alloc, .params = params, .encoding = .phc },
|
||||
&buf,
|
||||
io,
|
||||
);
|
||||
try strVerify(str, password, verify_options);
|
||||
}
|
||||
|
|
@ -720,3 +767,23 @@ test "kdf fast" {
|
|||
try std.testing.expectEqualSlices(u8, &dk, v.want);
|
||||
}
|
||||
}
|
||||
|
||||
test "strHashWithSalt deterministic" {
|
||||
const alloc = std.testing.allocator;
|
||||
const password = "testpass";
|
||||
const salt: [default_salt_len]u8 = "0123456789abcdef0123456789abcdef".*;
|
||||
const params: Params = .{ .ln = 1, .r = 1, .p = 1 };
|
||||
|
||||
var buf1: [128]u8 = undefined;
|
||||
var buf2: [128]u8 = undefined;
|
||||
|
||||
const str1 = try strHashWithSalt(password, .{ .allocator = alloc, .params = params, .encoding = .phc }, &buf1, &salt);
|
||||
const str2 = try strHashWithSalt(password, .{ .allocator = alloc, .params = params, .encoding = .phc }, &buf2, &salt);
|
||||
try std.testing.expectEqualStrings(str1, str2);
|
||||
try strVerify(str1, password, .{ .allocator = alloc });
|
||||
|
||||
const str3 = try strHashWithSalt(password, .{ .allocator = alloc, .params = params, .encoding = .crypt }, &buf1, &salt);
|
||||
const str4 = try strHashWithSalt(password, .{ .allocator = alloc, .params = params, .encoding = .crypt }, &buf2, &salt);
|
||||
try std.testing.expectEqualStrings(str3, str4);
|
||||
try strVerify(str3, password, .{ .allocator = alloc });
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue