authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-07 20:04:26+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-07 20:04:26+01:00
log62d6bbc7dc36dbb2f45da08e767076157deeb259
tree6f9e22b97a6cfee83aecf3fcc3bc5cd5f2a76806
parent867501d9d2f757f99af59b1904b251aa63262e23
parent006afece53af66401b941e25eba03efab6e9251f

Merge pull request 'std.Io: implement entropy (randomness)' (#30709) from random into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30709

66 files changed, 1301 insertions(+), 803 deletions(-)

build.zig+1-9
...@@ -593,15 +593,7 @@ pub fn build(b: *std.Build) !void {...@@ -593,15 +593,7 @@ pub fn build(b: *std.Build) !void {
593 .x86_64 => 3_756_422_348,593 .x86_64 => 3_756_422_348,
594 else => 3_800_000_000,594 else => 3_800_000_000,
595 },595 },
596 .linux => switch (b.graph.host.result.cpu.arch) {596 .linux => 6_800_000_000,
597 .aarch64 => 6_732_817_203,
598 .loongarch64 => 3_216_349_593,
599 .powerpc64le => 3_090_179_276,
600 .riscv64 => 4_052_670_054,
601 .s390x => 3_652_514_201,
602 .x86_64 => 3_249_546_854,
603 else => 6_800_000_000,
604 },
605 .macos => switch (b.graph.host.result.cpu.arch) {597 .macos => switch (b.graph.host.result.cpu.arch) {
606 .aarch64 => 8_273_795_481,598 .aarch64 => 8_273_795_481,
607 else => 8_300_000_000,599 else => 8_300_000_000,
lib/compiler/aro/aro/Driver.zig+2-1
...@@ -1217,11 +1217,12 @@ pub fn getDepFileName(d: *Driver, source: Source, buf: *[std.fs.max_name_bytes]u...@@ -1217,11 +1217,12 @@ pub fn getDepFileName(d: *Driver, source: Source, buf: *[std.fs.max_name_bytes]u
1217}1217}
12181218
1219fn getRandomFilename(d: *Driver, buf: *[std.fs.max_name_bytes]u8, extension: []const u8) ![]const u8 {1219fn getRandomFilename(d: *Driver, buf: *[std.fs.max_name_bytes]u8, extension: []const u8) ![]const u8 {
1220 const io = d.comp.io;
1220 const random_bytes_count = 12;1221 const random_bytes_count = 12;
1221 const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);1222 const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);
12221223
1223 var random_bytes: [random_bytes_count]u8 = undefined;1224 var random_bytes: [random_bytes_count]u8 = undefined;
1224 std.crypto.random.bytes(&random_bytes);1225 io.random(&random_bytes);
1225 var random_name: [sub_path_len]u8 = undefined;1226 var random_name: [sub_path_len]u8 = undefined;
1226 _ = std.fs.base64_encoder.encode(&random_name, &random_bytes);1227 _ = std.fs.base64_encoder.encode(&random_name, &random_bytes);
12271228
lib/compiler/build_runner.zig-1
...@@ -21,7 +21,6 @@ pub const dependencies = @import("@dependencies");...@@ -21,7 +21,6 @@ pub const dependencies = @import("@dependencies");
21pub const std_options: std.Options = .{21pub const std_options: std.Options = .{
22 .side_channels_mitigations = .none,22 .side_channels_mitigations = .none,
23 .http_disable_tls = true,23 .http_disable_tls = true,
24 .crypto_fork_safety = false,
25};24};
2625
27pub fn main(init: process.Init.Minimal) !void {26pub fn main(init: process.Init.Minimal) !void {
lib/std/Build/Step.zig+1-6
...@@ -111,12 +111,7 @@ pub const TestResults = struct {...@@ -111,12 +111,7 @@ pub const TestResults = struct {
111pub const MakeOptions = struct {111pub const MakeOptions = struct {
112 progress_node: std.Progress.Node,112 progress_node: std.Progress.Node,
113 watch: bool,113 watch: bool,
114 web_server: switch (builtin.target.cpu.arch) {114 web_server: ?*Build.WebServer,
115 else => ?*Build.WebServer,
116 // WASM code references `Build.abi` which happens to incidentally reference this type, but
117 // it currently breaks because `std.net.Address` doesn't work there. Work around for now.
118 .wasm32 => void,
119 },
120 /// If set, this is a timeout to enforce on all individual unit tests, in nanoseconds.115 /// If set, this is a timeout to enforce on all individual unit tests, in nanoseconds.
121 unit_test_timeout_ns: ?u64,116 unit_test_timeout_ns: ?u64,
122 /// Not to be confused with `Build.allocator`, which is an alias of `Build.graph.arena`.117 /// Not to be confused with `Build.allocator`, which is an alias of `Build.graph.arena`.
lib/std/Build/Step/Compile.zig+19-8
...@@ -1706,18 +1706,29 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -1706,18 +1706,29 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
1706 // The args file is already present from a previous run.1706 // The args file is already present from a previous run.
1707 } else |err| switch (err) {1707 } else |err| switch (err) {
1708 error.FileNotFound => {1708 error.FileNotFound => {
1709 try b.cache_root.handle.createDirPath(io, "tmp");1709 var af = b.cache_root.handle.createFileAtomic(io, args_file, .{
1710 const rand_int = std.crypto.random.int(u64);1710 .replace = false,
1711 const tmp_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);1711 .make_path = true,
1712 try b.cache_root.handle.writeFile(io, .{ .sub_path = tmp_path, .data = args });1712 }) catch |e| return step.fail("failed creating tmp args file {f}{s}: {t}", .{
1713 defer b.cache_root.handle.deleteFile(io, tmp_path) catch {1713 b.cache_root, args_file, e,
1714 // It's fine if the temporary file can't be cleaned up.1714 });
1715 defer af.deinit(io);
1716
1717 af.file.writeStreamingAll(io, args) catch |e| {
1718 return step.fail("failed writing args data to tmp file {f}{s}: {t}", .{
1719 b.cache_root, args_file, e,
1720 });
1715 };1721 };
1716 b.cache_root.handle.rename(tmp_path, b.cache_root.handle, args_file, io) catch |rename_err| switch (rename_err) {1722 // Note we can't clean up this file, not even after build
1723 // success, because that might interfere with another build
1724 // process that needs the same file.
1725 af.link(io) catch |e| switch (e) {
1717 error.PathAlreadyExists => {1726 error.PathAlreadyExists => {
1718 // The args file was created by another concurrent build process.1727 // The args file was created by another concurrent build process.
1719 },1728 },
1720 else => |other_err| return other_err,1729 else => |other_err| return step.fail("failed linking tmp file {f}{s}: {t}", .{
1730 b.cache_root, args_file, other_err,
1731 }),
1721 };1732 };
1722 },1733 },
1723 else => |other_err| return other_err,1734 else => |other_err| return other_err,
lib/std/Build/Step/Options.zig+14-32
...@@ -476,46 +476,28 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {...@@ -476,46 +476,28 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void {
476 return;476 return;
477 } else |outer_err| switch (outer_err) {477 } else |outer_err| switch (outer_err) {
478 error.FileNotFound => {478 error.FileNotFound => {
479 const sub_dirname = fs.path.dirname(sub_path).?;479 var atomic_file = b.cache_root.handle.createFileAtomic(io, sub_path, .{
480 b.cache_root.handle.createDirPath(io, sub_dirname) catch |e|480 .replace = false,
481 return step.fail("unable to make path '{f}{s}': {t}", .{ b.cache_root, sub_dirname, e });481 .make_path = true,
482482 }) catch |err| return step.fail("failed to create temporary path for '{f}{s}': {t}", .{
483 const rand_int = std.crypto.random.int(u64);483 b.cache_root, sub_path, err,
484 const tmp_sub_path = "tmp" ++ fs.path.sep_str ++484 });
485 std.fmt.hex(rand_int) ++ fs.path.sep_str ++485 defer atomic_file.deinit(io);
486 basename;
487 const tmp_sub_path_dirname = fs.path.dirname(tmp_sub_path).?;
488
489 b.cache_root.handle.createDirPath(io, tmp_sub_path_dirname) catch |err| {
490 return step.fail("unable to make temporary directory '{f}{s}': {t}", .{
491 b.cache_root, tmp_sub_path_dirname, err,
492 });
493 };
494486
495 b.cache_root.handle.writeFile(io, .{ .sub_path = tmp_sub_path, .data = options.contents.items }) catch |err| {487 atomic_file.file.writeStreamingAll(io, options.contents.items) catch |err| {
496 return step.fail("unable to write options to '{f}{s}': {t}", .{488 return step.fail("failed to write options to temporary path for '{f}{s}': {t}", .{
497 b.cache_root, tmp_sub_path, err,489 b.cache_root, sub_path, err,
498 });490 });
499 };491 };
500492
501 b.cache_root.handle.rename(tmp_sub_path, b.cache_root.handle, sub_path, io) catch |err| switch (err) {493 atomic_file.link(io) catch |err| switch (err) {
502 error.PathAlreadyExists => {494 error.PathAlreadyExists => {
503 // Other process beat us to it. Clean up the temp file.
504 b.cache_root.handle.deleteFile(io, tmp_sub_path) catch |e| {
505 try step.addError("warning: unable to delete temp file '{f}{s}': {t}", .{
506 b.cache_root, tmp_sub_path, e,
507 });
508 };
509 step.result_cached = true;495 step.result_cached = true;
510 return;496 return;
511 },497 },
512 else => {498 else => return step.fail("failed to link temporary file into '{f}{s}': {t}", .{
513 return step.fail("unable to rename options from '{f}{s}' to '{f}{s}': {t}", .{499 b.cache_root, sub_path, err,
514 b.cache_root, tmp_sub_path,500 }),
515 b.cache_root, sub_path,
516 err,
517 });
518 },
519 };501 };
520 },502 },
521 else => |e| return step.fail("unable to access options file '{f}{s}': {t}", .{503 else => |e| return step.fail("unable to access options file '{f}{s}': {t}", .{
lib/std/Build/Step/Run.zig+4-2
...@@ -984,7 +984,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -984,7 +984,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
984 };984 };
985985
986 // We do not know the final output paths yet, use temp paths to run the command.986 // We do not know the final output paths yet, use temp paths to run the command.
987 const rand_int = std.crypto.random.int(u64);987 var rand_int: u64 = undefined;
988 io.random(@ptrCast(&rand_int));
988 const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);989 const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
989990
990 for (output_placeholders.items) |placeholder| {991 for (output_placeholders.items) |placeholder| {
...@@ -1128,7 +1129,8 @@ pub fn rerunInFuzzMode(...@@ -1128,7 +1129,8 @@ pub fn rerunInFuzzMode(
1128 }1129 }
11291130
1130 const has_side_effects = false;1131 const has_side_effects = false;
1131 const rand_int = std.crypto.random.int(u64);1132 var rand_int: u64 = undefined;
1133 io.random(@ptrCast(&rand_int));
1132 const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);1134 const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
1133 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, .{1135 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, .{
1134 .progress_node = prog_node,1136 .progress_node = prog_node,
lib/std/Build/Step/WriteFile.zig+2-1
...@@ -293,7 +293,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -293,7 +293,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
293 .tmp => {293 .tmp => {
294 step.result_cached = false;294 step.result_cached = false;
295295
296 const rand_int = std.crypto.random.int(u64);296 var rand_int: u64 = undefined;
297 io.random(@ptrCast(&rand_int));
297 const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);298 const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
298299
299 write_file.generated_directory.path = try b.cache_root.join(arena, &.{tmp_dir_sub_path});300 write_file.generated_directory.path = try b.cache_root.join(arena, &.{tmp_dir_sub_path});
lib/std/Io.zig+37
...@@ -676,6 +676,7 @@ pub const VTable = struct {...@@ -676,6 +676,7 @@ pub const VTable = struct {
676 dirDeleteFile: *const fn (?*anyopaque, Dir, []const u8) Dir.DeleteFileError!void,676 dirDeleteFile: *const fn (?*anyopaque, Dir, []const u8) Dir.DeleteFileError!void,
677 dirDeleteDir: *const fn (?*anyopaque, Dir, []const u8) Dir.DeleteDirError!void,677 dirDeleteDir: *const fn (?*anyopaque, Dir, []const u8) Dir.DeleteDirError!void,
678 dirRename: *const fn (?*anyopaque, old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) Dir.RenameError!void,678 dirRename: *const fn (?*anyopaque, old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) Dir.RenameError!void,
679 dirRenamePreserve: *const fn (?*anyopaque, old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) Dir.RenamePreserveError!void,
679 dirSymLink: *const fn (?*anyopaque, Dir, target_path: []const u8, sym_link_path: []const u8, Dir.SymLinkFlags) Dir.SymLinkError!void,680 dirSymLink: *const fn (?*anyopaque, Dir, target_path: []const u8, sym_link_path: []const u8, Dir.SymLinkFlags) Dir.SymLinkError!void,
680 dirReadLink: *const fn (?*anyopaque, Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize,681 dirReadLink: *const fn (?*anyopaque, Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize,
681 dirSetOwner: *const fn (?*anyopaque, Dir, ?File.Uid, ?File.Gid) Dir.SetOwnerError!void,682 dirSetOwner: *const fn (?*anyopaque, Dir, ?File.Uid, ?File.Gid) Dir.SetOwnerError!void,
...@@ -731,6 +732,9 @@ pub const VTable = struct {...@@ -731,6 +732,9 @@ pub const VTable = struct {
731 now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp,732 now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp,
732 sleep: *const fn (?*anyopaque, Timeout) SleepError!void,733 sleep: *const fn (?*anyopaque, Timeout) SleepError!void,
733734
735 random: *const fn (?*anyopaque, buffer: []u8) void,
736 randomSecure: *const fn (?*anyopaque, buffer: []u8) RandomSecureError!void,
737
734 netListenIp: *const fn (?*anyopaque, address: net.IpAddress, net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server,738 netListenIp: *const fn (?*anyopaque, address: net.IpAddress, net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server,
735 netAccept: *const fn (?*anyopaque, server: net.Socket.Handle) net.Server.AcceptError!net.Stream,739 netAccept: *const fn (?*anyopaque, server: net.Socket.Handle) net.Server.AcceptError!net.Stream,
736 netBindIp: *const fn (?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket,740 netBindIp: *const fn (?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket,
...@@ -2242,3 +2246,36 @@ pub fn tryLockStderr(io: Io, buffer: []u8, terminal_mode: ?Terminal.Mode) Cancel...@@ -2242,3 +2246,36 @@ pub fn tryLockStderr(io: Io, buffer: []u8, terminal_mode: ?Terminal.Mode) Cancel
2242pub fn unlockStderr(io: Io) void {2246pub fn unlockStderr(io: Io) void {
2243 return io.vtable.unlockStderr(io.userdata);2247 return io.vtable.unlockStderr(io.userdata);
2244}2248}
2249
2250/// Obtains entropy from a cryptographically secure pseudo-random number
2251/// generator.
2252///
2253/// The implementation *may* store RNG state in process memory and use it to
2254/// fill `buffer`.
2255///
2256/// The randomness is seeded by `randomSecure`, or a less secure mechanism upon
2257/// failure.
2258///
2259/// Threadsafe.
2260///
2261/// See also `randomSecure`.
2262pub fn random(io: Io, buffer: []u8) void {
2263 return io.vtable.random(io.userdata, buffer);
2264}
2265
2266pub const RandomSecureError = error{EntropyUnavailable} || Cancelable;
2267
2268/// Obtains cryptographically secure entropy from outside the process.
2269///
2270/// Always makes a syscall, or otherwise avoids dependency on process memory,
2271/// in order to obtain fresh randomness. Does not rely on stored RNG state.
2272///
2273/// Does not have any fallback mechanisms; returns `error.EntropyUnavailable`
2274/// if any problems occur.
2275///
2276/// Threadsafe.
2277///
2278/// See also `random`.
2279pub fn randomSecure(io: Io, buffer: []u8) RandomSecureError!void {
2280 return io.vtable.randomSecure(io.userdata, buffer);
2281}
lib/std/Io/Dir.zig+43-9
...@@ -936,10 +936,9 @@ pub fn deleteDirAbsolute(io: Io, absolute_path: []const u8) DeleteDirError!void...@@ -936,10 +936,9 @@ pub fn deleteDirAbsolute(io: Io, absolute_path: []const u8) DeleteDirError!void
936pub const RenameError = error{936pub const RenameError = error{
937 /// In WASI, this error may occur when the file descriptor does937 /// In WASI, this error may occur when the file descriptor does
938 /// not hold the required rights to rename a resource by path relative to it.938 /// not hold the required rights to rename a resource by path relative to it.
939 ///
940 /// On Windows, this error may be returned instead of PathAlreadyExists when
941 /// renaming a directory over an existing directory.
942 AccessDenied,939 AccessDenied,
940 /// Attempted to replace a nonempty directory.
941 DirNotEmpty,
943 PermissionDenied,942 PermissionDenied,
944 FileBusy,943 FileBusy,
945 DiskQuota,944 DiskQuota,
...@@ -950,9 +949,8 @@ pub const RenameError = error{...@@ -950,9 +949,8 @@ pub const RenameError = error{
950 NotDir,949 NotDir,
951 SystemResources,950 SystemResources,
952 NoSpaceLeft,951 NoSpaceLeft,
953 PathAlreadyExists,
954 ReadOnlyFileSystem,952 ReadOnlyFileSystem,
955 RenameAcrossMountPoints,953 CrossDevice,
956 NoDevice,954 NoDevice,
957 SharingViolation,955 SharingViolation,
958 PipeBusy,956 PipeBusy,
...@@ -964,6 +962,7 @@ pub const RenameError = error{...@@ -964,6 +962,7 @@ pub const RenameError = error{
964 /// intercepts file system operations and makes them significantly slower962 /// intercepts file system operations and makes them significantly slower
965 /// in addition to possibly failing with this error code.963 /// in addition to possibly failing with this error code.
966 AntivirusInterference,964 AntivirusInterference,
965 HardwareFailure,
967} || PathNameError || Io.Cancelable || Io.UnexpectedError;966} || PathNameError || Io.Cancelable || Io.UnexpectedError;
968967
969/// Change the name or location of a file or directory.968/// Change the name or location of a file or directory.
...@@ -973,9 +972,9 @@ pub const RenameError = error{...@@ -973,9 +972,9 @@ pub const RenameError = error{
973/// Renaming a file over an existing directory or a directory over an existing972/// Renaming a file over an existing directory or a directory over an existing
974/// file will fail with `error.IsDir` or `error.NotDir`973/// file will fail with `error.IsDir` or `error.NotDir`
975///974///
976/// On Windows, both paths should be encoded as [WTF-8](https://wtf-8.codeberg.page/).975/// * On Windows, both paths should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
977/// On WASI, both paths should be encoded as valid UTF-8.976/// * On WASI, both paths should be encoded as valid UTF-8.
978/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.977/// * On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
979pub fn rename(978pub fn rename(
980 old_dir: Dir,979 old_dir: Dir,
981 old_sub_path: []const u8,980 old_sub_path: []const u8,
...@@ -993,6 +992,39 @@ pub fn renameAbsolute(old_path: []const u8, new_path: []const u8, io: Io) Rename...@@ -993,6 +992,39 @@ pub fn renameAbsolute(old_path: []const u8, new_path: []const u8, io: Io) Rename
993 return io.vtable.dirRename(io.userdata, my_cwd, old_path, my_cwd, new_path);992 return io.vtable.dirRename(io.userdata, my_cwd, old_path, my_cwd, new_path);
994}993}
995994
995pub const RenamePreserveError = error{
996 /// In WASI, this error may occur when the file descriptor does
997 /// not hold the required rights to rename a resource by path relative to it.
998 ///
999 /// On Windows, this error may be returned instead of PathAlreadyExists when
1000 /// renaming a directory over an existing directory.
1001 AccessDenied,
1002 PathAlreadyExists,
1003 /// Operating system or file system does not support atomic nonreplacing
1004 /// rename.
1005 OperationUnsupported,
1006} || RenameError;
1007
1008/// Change the name or location of a file or directory.
1009///
1010/// If `new_sub_path` already exists, `error.PathAlreadyExists` will be returned.
1011///
1012/// Renaming a file over an existing directory or a directory over an existing
1013/// file will fail with `error.IsDir` or `error.NotDir`
1014///
1015/// * On Windows, both paths should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
1016/// * On WASI, both paths should be encoded as valid UTF-8.
1017/// * On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
1018pub fn renamePreserve(
1019 old_dir: Dir,
1020 old_sub_path: []const u8,
1021 new_dir: Dir,
1022 new_sub_path: []const u8,
1023 io: Io,
1024) RenamePreserveError!void {
1025 return io.vtable.dirRenamePreserve(io.userdata, old_dir, old_sub_path, new_dir, new_sub_path);
1026}
1027
996pub const HardLinkOptions = File.HardLinkOptions;1028pub const HardLinkOptions = File.HardLinkOptions;
9971029
998pub const HardLinkError = File.HardLinkError;1030pub const HardLinkError = File.HardLinkError;
...@@ -1098,8 +1130,10 @@ pub fn symLinkAtomic(...@@ -1098,8 +1130,10 @@ pub fn symLinkAtomic(
10981130
1099 const temp_path = temp_path_buf[0..temp_path_len];1131 const temp_path = temp_path_buf[0..temp_path_len];
11001132
1133 var random_integer: u64 = undefined;
1134
1101 while (true) {1135 while (true) {
1102 const random_integer = std.crypto.random.int(u64);1136 io.random(@ptrCast(&random_integer));
1103 temp_path[dirname.len + 1 ..][0..rand_len].* = std.fmt.hex(random_integer);1137 temp_path[dirname.len + 1 ..][0..rand_len].* = std.fmt.hex(random_integer);
11041138
1105 if (dir.symLink(io, target_path, temp_path, flags)) {1139 if (dir.symLink(io, target_path, temp_path, flags)) {
lib/std/Io/File.zig+2-2
...@@ -709,7 +709,7 @@ pub fn realPath(file: File, io: Io, out_buffer: []u8) RealPathError!usize {...@@ -709,7 +709,7 @@ pub fn realPath(file: File, io: Io, out_buffer: []u8) RealPathError!usize {
709}709}
710710
711pub const HardLinkOptions = struct {711pub const HardLinkOptions = struct {
712 follow_symlinks: bool = true,712 follow_symlinks: bool = false,
713};713};
714714
715pub const HardLinkError = error{715pub const HardLinkError = error{
...@@ -726,7 +726,7 @@ pub const HardLinkError = error{...@@ -726,7 +726,7 @@ pub const HardLinkError = error{
726 SystemResources,726 SystemResources,
727 NoSpaceLeft,727 NoSpaceLeft,
728 ReadOnlyFileSystem,728 ReadOnlyFileSystem,
729 NotSameFileSystem,729 CrossDevice,
730 NotDir,730 NotDir,
731} || Io.Cancelable || Dir.PathNameError || Io.UnexpectedError;731} || Io.Cancelable || Dir.PathNameError || Io.UnexpectedError;
732732
lib/std/Io/File/Atomic.zig+6-3
...@@ -37,10 +37,14 @@ pub fn deinit(af: *Atomic, io: Io) void {...@@ -37,10 +37,14 @@ pub fn deinit(af: *Atomic, io: Io) void {
37 af.* = undefined;37 af.* = undefined;
38}38}
3939
40pub const LinkError = Dir.HardLinkError;40pub const LinkError = File.HardLinkError || Dir.RenamePreserveError;
4141
42/// Atomically materializes the file into place, failing with42/// Atomically materializes the file into place, failing with
43/// `error.PathAlreadyExists` if something already exists there.43/// `error.PathAlreadyExists` if something already exists there.
44///
45/// If this operation could not be done with an unnamed temporary file, the
46/// named temporary file will be deleted in a following operation, which may
47/// independently fail. The result of that operation is stored in `delete_err`.
44pub fn link(af: *Atomic, io: Io) LinkError!void {48pub fn link(af: *Atomic, io: Io) LinkError!void {
45 if (af.file_exists) {49 if (af.file_exists) {
46 if (af.file_open) {50 if (af.file_open) {
...@@ -48,8 +52,7 @@ pub fn link(af: *Atomic, io: Io) LinkError!void {...@@ -48,8 +52,7 @@ pub fn link(af: *Atomic, io: Io) LinkError!void {
48 af.file_open = false;52 af.file_open = false;
49 }53 }
50 const tmp_sub_path = std.fmt.hex(af.file_basename_hex);54 const tmp_sub_path = std.fmt.hex(af.file_basename_hex);
51 try af.dir.hardLink(&tmp_sub_path, af.dir, af.dest_sub_path, io, .{});55 try af.dir.renamePreserve(&tmp_sub_path, af.dir, af.dest_sub_path, io);
52 af.dir.deleteFile(io, &tmp_sub_path) catch {};
53 af.file_exists = false;56 af.file_exists = false;
54 } else {57 } else {
55 assert(af.file_open);58 assert(af.file_open);
lib/std/Io/Threaded.zig+620-50
...@@ -65,6 +65,22 @@ argv0: Argv0,...@@ -65,6 +65,22 @@ argv0: Argv0,
65environ: Environ,65environ: Environ,
6666
67null_file: NullFile = .{},67null_file: NullFile = .{},
68random_file: RandomFile = .{},
69
70csprng: Csprng = .{},
71
72pub const Csprng = struct {
73 rng: std.Random.DefaultCsprng = .{
74 .state = undefined,
75 .offset = std.math.maxInt(usize),
76 },
77
78 pub const seed_len = std.Random.DefaultCsprng.secret_seed_length;
79
80 pub fn isInitialized(c: *const Csprng) bool {
81 return c.rng.offset != std.math.maxInt(usize);
82 }
83};
6884
69pub const Argv0 = switch (native_os) {85pub const Argv0 = switch (native_os) {
70 .openbsd, .haiku => struct {86 .openbsd, .haiku => struct {
...@@ -151,6 +167,15 @@ pub const NullFile = switch (native_os) {...@@ -151,6 +167,15 @@ pub const NullFile = switch (native_os) {
151 },167 },
152};168};
153169
170pub const RandomFile = switch (native_os) {
171 .windows => NullFile,
172 else => if (use_dev_urandom) NullFile else struct {
173 fn deinit(this: @This()) void {
174 _ = this;
175 }
176 },
177};
178
154pub const Pid = if (native_os == .linux) enum(posix.pid_t) {179pub const Pid = if (native_os == .linux) enum(posix.pid_t) {
155 unknown = 0,180 unknown = 0,
156 _,181 _,
...@@ -585,6 +610,8 @@ const Thread = struct {...@@ -585,6 +610,8 @@ const Thread = struct {
585 /// Always released when `Status.cancelation` is set to `.parked`.610 /// Always released when `Status.cancelation` is set to `.parked`.
586 futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn,611 futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn,
587612
613 csprng: Csprng,
614
588 const Handle = Handle: {615 const Handle = Handle: {
589 if (std.Thread.use_pthreads) break :Handle std.c.pthread_t;616 if (std.Thread.use_pthreads) break :Handle std.c.pthread_t;
590 if (builtin.target.os.tag == .windows) break :Handle windows.HANDLE;617 if (builtin.target.os.tag == .windows) break :Handle windows.HANDLE;
...@@ -1285,6 +1312,7 @@ pub fn deinit(t: *Threaded) void {...@@ -1285,6 +1312,7 @@ pub fn deinit(t: *Threaded) void {
1285 if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null);1312 if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null);
1286 }1313 }
1287 t.null_file.deinit();1314 t.null_file.deinit();
1315 t.random_file.deinit();
1288 t.* = undefined;1316 t.* = undefined;
1289}1317}
12901318
...@@ -1313,6 +1341,7 @@ fn worker(t: *Threaded) void {...@@ -1313,6 +1341,7 @@ fn worker(t: *Threaded) void {
1313 }),1341 }),
1314 .cancel_protection = .unblocked,1342 .cancel_protection = .unblocked,
1315 .futex_waiter = undefined,1343 .futex_waiter = undefined,
1344 .csprng = .{},
1316 };1345 };
1317 Thread.current = &thread;1346 Thread.current = &thread;
13181347
...@@ -1413,6 +1442,7 @@ pub fn io(t: *Threaded) Io {...@@ -1413,6 +1442,7 @@ pub fn io(t: *Threaded) Io {
1413 .dirDeleteFile = dirDeleteFile,1442 .dirDeleteFile = dirDeleteFile,
1414 .dirDeleteDir = dirDeleteDir,1443 .dirDeleteDir = dirDeleteDir,
1415 .dirRename = dirRename,1444 .dirRename = dirRename,
1445 .dirRenamePreserve = dirRenamePreserve,
1416 .dirSymLink = dirSymLink,1446 .dirSymLink = dirSymLink,
1417 .dirReadLink = dirReadLink,1447 .dirReadLink = dirReadLink,
1418 .dirSetOwner = dirSetOwner,1448 .dirSetOwner = dirSetOwner,
...@@ -1466,6 +1496,9 @@ pub fn io(t: *Threaded) Io {...@@ -1466,6 +1496,9 @@ pub fn io(t: *Threaded) Io {
1466 .now = now,1496 .now = now,
1467 .sleep = sleep,1497 .sleep = sleep,
14681498
1499 .random = random,
1500 .randomSecure = randomSecure,
1501
1469 .netListenIp = switch (native_os) {1502 .netListenIp = switch (native_os) {
1470 .windows => netListenIpWindows,1503 .windows => netListenIpWindows,
1471 else => netListenIpPosix,1504 else => netListenIpPosix,
...@@ -1561,6 +1594,7 @@ pub fn ioBasic(t: *Threaded) Io {...@@ -1561,6 +1594,7 @@ pub fn ioBasic(t: *Threaded) Io {
1561 .dirDeleteFile = dirDeleteFile,1594 .dirDeleteFile = dirDeleteFile,
1562 .dirDeleteDir = dirDeleteDir,1595 .dirDeleteDir = dirDeleteDir,
1563 .dirRename = dirRename,1596 .dirRename = dirRename,
1597 .dirRenamePreserve = dirRenamePreserve,
1564 .dirSymLink = dirSymLink,1598 .dirSymLink = dirSymLink,
1565 .dirReadLink = dirReadLink,1599 .dirReadLink = dirReadLink,
1566 .dirSetOwner = dirSetOwner,1600 .dirSetOwner = dirSetOwner,
...@@ -1614,6 +1648,9 @@ pub fn ioBasic(t: *Threaded) Io {...@@ -1614,6 +1648,9 @@ pub fn ioBasic(t: *Threaded) Io {
1614 .now = now,1648 .now = now,
1615 .sleep = sleep,1649 .sleep = sleep,
16161650
1651 .random = random,
1652 .randomSecure = randomSecure,
1653
1617 .netListenIp = netListenIpUnavailable,1654 .netListenIp = netListenIpUnavailable,
1618 .netListenUnix = netListenUnixUnavailable,1655 .netListenUnix = netListenUnixUnavailable,
1619 .netAccept = netAcceptUnavailable,1656 .netAccept = netAcceptUnavailable,
...@@ -1704,6 +1741,23 @@ const linux_copy_file_range_use_c = std.c.versionCheck(if (builtin.abi.isAndroid...@@ -1704,6 +1741,23 @@ const linux_copy_file_range_use_c = std.c.versionCheck(if (builtin.abi.isAndroid
1704});1741});
1705const linux_copy_file_range_sys = if (linux_copy_file_range_use_c) std.c else std.os.linux;1742const linux_copy_file_range_sys = if (linux_copy_file_range_use_c) std.c else std.os.linux;
17061743
1744const statx_use_c = std.c.versionCheck(if (builtin.abi.isAndroid())
1745 .{ .major = 30, .minor = 0, .patch = 0 }
1746else
1747 .{ .major = 2, .minor = 28, .patch = 0 });
1748
1749const use_libc_getrandom = std.c.versionCheck(if (builtin.abi.isAndroid()) .{
1750 .major = 28,
1751 .minor = 0,
1752 .patch = 0,
1753} else .{
1754 .major = 2,
1755 .minor = 25,
1756 .patch = 0,
1757});
1758
1759const use_dev_urandom = @TypeOf(posix.system.getrandom) == void and native_os == .linux;
1760
1707fn async(1761fn async(
1708 userdata: ?*anyopaque,1762 userdata: ?*anyopaque,
1709 result: []u8,1763 result: []u8,
...@@ -2342,11 +2396,11 @@ fn dirCreateDirPath(...@@ -2342,11 +2396,11 @@ fn dirCreateDirPath(
2342 status = .created;2396 status = .created;
2343 } else |err| switch (err) {2397 } else |err| switch (err) {
2344 error.PathAlreadyExists => {2398 error.PathAlreadyExists => {
2345 // stat the file and return an error if it's not a directory2399 // It is important to return an error if it's not a directory
2346 // this is important because otherwise a dangling symlink2400 // because otherwise a dangling symlink could cause an infinite
2347 // could cause an infinite loop2401 // loop.
2348 const fstat = try dirStatFile(t, dir, component.path, .{});2402 const kind = try filePathKind(t, dir, component.path);
2349 if (fstat.kind != .directory) return error.NotDir;2403 if (kind != .directory) return error.NotDir;
2350 },2404 },
2351 error.FileNotFound => |e| {2405 error.FileNotFound => |e| {
2352 component = it.previous() orelse return e;2406 component = it.previous() orelse return e;
...@@ -2538,11 +2592,7 @@ fn dirStatFileLinux(...@@ -2538,11 +2592,7 @@ fn dirStatFileLinux(
2538 const t: *Threaded = @ptrCast(@alignCast(userdata));2592 const t: *Threaded = @ptrCast(@alignCast(userdata));
2539 _ = t;2593 _ = t;
2540 const linux = std.os.linux;2594 const linux = std.os.linux;
2541 const use_c = std.c.versionCheck(if (builtin.abi.isAndroid())2595 const sys = if (statx_use_c) std.c else std.os.linux;
2542 .{ .major = 30, .minor = 0, .patch = 0 }
2543 else
2544 .{ .major = 2, .minor = 28, .patch = 0 });
2545 const sys = if (use_c) std.c else std.os.linux;
25462596
2547 var path_buffer: [posix.PATH_MAX]u8 = undefined;2597 var path_buffer: [posix.PATH_MAX]u8 = undefined;
2548 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);2598 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
...@@ -2691,6 +2741,35 @@ fn dirStatFileWasi(...@@ -2691,6 +2741,35 @@ fn dirStatFileWasi(
2691 }2741 }
2692}2742}
26932743
2744fn filePathKind(t: *Threaded, dir: Dir, sub_path: []const u8) !File.Kind {
2745 if (native_os == .linux) {
2746 var path_buffer: [posix.PATH_MAX]u8 = undefined;
2747 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
2748
2749 const linux = std.os.linux;
2750 const syscall: Syscall = try .start();
2751 while (true) {
2752 var statx = std.mem.zeroes(linux.Statx);
2753 switch (linux.errno(linux.statx(dir.handle, sub_path_posix, 0, .{ .TYPE = true }, &statx))) {
2754 .SUCCESS => {
2755 syscall.finish();
2756 if (!statx.mask.TYPE) return error.Unexpected;
2757 return statxKind(statx.mode);
2758 },
2759 .INTR => {
2760 try syscall.checkCancel();
2761 continue;
2762 },
2763 .NOMEM => return syscall.fail(error.SystemResources),
2764 else => |err| return syscall.unexpectedErrno(err),
2765 }
2766 }
2767 }
2768
2769 const stat = try dirStatFile(t, dir, sub_path, .{});
2770 return stat.kind;
2771}
2772
2694fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 {2773fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 {
2695 const t: *Threaded = @ptrCast(@alignCast(userdata));2774 const t: *Threaded = @ptrCast(@alignCast(userdata));
26962775
...@@ -2778,11 +2857,7 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {...@@ -2778,11 +2857,7 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
2778 const t: *Threaded = @ptrCast(@alignCast(userdata));2857 const t: *Threaded = @ptrCast(@alignCast(userdata));
2779 _ = t;2858 _ = t;
2780 const linux = std.os.linux;2859 const linux = std.os.linux;
2781 const use_c = std.c.versionCheck(if (builtin.abi.isAndroid())2860 const sys = if (statx_use_c) std.c else std.os.linux;
2782 .{ .major = 30, .minor = 0, .patch = 0 }
2783 else
2784 .{ .major = 2, .minor = 28, .patch = 0 });
2785 const sys = if (use_c) std.c else std.os.linux;
27862861
2787 const syscall: Syscall = try .start();2862 const syscall: Syscall = try .start();
2788 while (true) {2863 while (true) {
...@@ -3450,7 +3525,7 @@ fn dirCreateFileAtomic(...@@ -3450,7 +3525,7 @@ fn dirCreateFileAtomic(
3450 if (dest_dirname) |dirname| {3525 if (dest_dirname) |dirname| {
3451 // This has a nice side effect of preemptively triggering EISDIR or3526 // This has a nice side effect of preemptively triggering EISDIR or
3452 // ENOENT, avoiding the ambiguity below.3527 // ENOENT, avoiding the ambiguity below.
3453 dir.createDirPath(t_io, dirname) catch |err| switch (err) {3528 if (options.make_path) dir.createDirPath(t_io, dirname) catch |err| switch (err) {
3454 // None of these make sense in this context.3529 // None of these make sense in this context.
3455 error.IsDir,3530 error.IsDir,
3456 error.Streaming,3531 error.Streaming,
...@@ -3553,8 +3628,9 @@ fn atomicFileInit(...@@ -3553,8 +3628,9 @@ fn atomicFileInit(
3553 dir: Dir,3628 dir: Dir,
3554 close_dir_on_deinit: bool,3629 close_dir_on_deinit: bool,
3555) Dir.CreateFileAtomicError!File.Atomic {3630) Dir.CreateFileAtomicError!File.Atomic {
3631 var random_integer: u64 = undefined;
3556 while (true) {3632 while (true) {
3557 const random_integer = std.crypto.random.int(u64);3633 t_io.random(@ptrCast(&random_integer));
3558 const tmp_sub_path = std.fmt.hex(random_integer);3634 const tmp_sub_path = std.fmt.hex(random_integer);
3559 const file = dir.createFile(t_io, &tmp_sub_path, .{3635 const file = dir.createFile(t_io, &tmp_sub_path, .{
3560 .permissions = permissions,3636 .permissions = permissions,
...@@ -3636,10 +3712,12 @@ fn dirOpenFilePosix(...@@ -3636,10 +3712,12 @@ fn dirOpenFilePosix(
3636 },3712 },
3637 };3713 };
36383714
3715 const mode: posix.mode_t = 0;
3716
3639 const fd: posix.fd_t = fd: {3717 const fd: posix.fd_t = fd: {
3640 const syscall: Syscall = try .start();3718 const syscall: Syscall = try .start();
3641 while (true) {3719 while (true) {
3642 const rc = openat_sym(dir.handle, sub_path_posix, os_flags, @as(posix.mode_t, 0));3720 const rc = openat_sym(dir.handle, sub_path_posix, os_flags, mode);
3643 switch (posix.errno(rc)) {3721 switch (posix.errno(rc)) {
3644 .SUCCESS => {3722 .SUCCESS => {
3645 syscall.finish();3723 syscall.finish();
...@@ -4068,9 +4146,11 @@ fn dirOpenDirPosix(...@@ -4068,9 +4146,11 @@ fn dirOpenDirPosix(
4068 if (@hasField(posix.O, "PATH") and !options.iterate)4146 if (@hasField(posix.O, "PATH") and !options.iterate)
4069 flags.PATH = true;4147 flags.PATH = true;
40704148
4149 const mode: posix.mode_t = 0;
4150
4071 const syscall: Syscall = try .start();4151 const syscall: Syscall = try .start();
4072 while (true) {4152 while (true) {
4073 const rc = openat_sym(dir.handle, sub_path_posix, flags, @as(usize, 0));4153 const rc = openat_sym(dir.handle, sub_path_posix, flags, mode);
4074 switch (posix.errno(rc)) {4154 switch (posix.errno(rc)) {
4075 .SUCCESS => {4155 .SUCCESS => {
4076 syscall.finish();4156 syscall.finish();
...@@ -5169,12 +5249,21 @@ fn fileHardLink(...@@ -5169,12 +5249,21 @@ fn fileHardLink(
5169 var new_path_buffer: [posix.PATH_MAX]u8 = undefined;5249 var new_path_buffer: [posix.PATH_MAX]u8 = undefined;
5170 const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);5250 const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);
51715251
5172 const flags: u32 = if (!options.follow_symlinks)5252 const flags: u32 = if (options.follow_symlinks)
5173 posix.AT.SYMLINK_NOFOLLOW | posix.AT.EMPTY_PATH5253 posix.AT.SYMLINK_FOLLOW | posix.AT.EMPTY_PATH
5174 else5254 else
5175 posix.AT.EMPTY_PATH;5255 posix.AT.EMPTY_PATH;
51765256
5177 return linkat(file.handle, "", new_dir.handle, new_sub_path_posix, flags);5257 return linkat(file.handle, "", new_dir.handle, new_sub_path_posix, flags) catch |err| switch (err) {
5258 error.FileNotFound => {
5259 if (options.follow_symlinks) return error.FileNotFound;
5260 var proc_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
5261 const proc_path = std.fmt.bufPrintSentinel(&proc_buf, "/proc/self/fd/{d}", .{file.handle}, 0) catch
5262 unreachable;
5263 return linkat(posix.AT.FDCWD, proc_path, new_dir.handle, new_sub_path_posix, posix.AT.SYMLINK_FOLLOW);
5264 },
5265 else => |e| return e,
5266 };
5178}5267}
51795268
5180fn linkat(5269fn linkat(
...@@ -5205,7 +5294,7 @@ fn linkat(...@@ -5205,7 +5294,7 @@ fn linkat(
5205 .NOTDIR => return syscall.fail(error.NotDir),5294 .NOTDIR => return syscall.fail(error.NotDir),
5206 .PERM => return syscall.fail(error.PermissionDenied),5295 .PERM => return syscall.fail(error.PermissionDenied),
5207 .ROFS => return syscall.fail(error.ReadOnlyFileSystem),5296 .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
5208 .XDEV => return syscall.fail(error.NotSameFileSystem),5297 .XDEV => return syscall.fail(error.CrossDevice),
5209 .ILSEQ => return syscall.fail(error.BadPathName),5298 .ILSEQ => return syscall.fail(error.BadPathName),
5210 .FAULT => |err| return syscall.errnoBug(err),5299 .FAULT => |err| return syscall.errnoBug(err),
5211 .INVAL => |err| return syscall.errnoBug(err),5300 .INVAL => |err| return syscall.errnoBug(err),
...@@ -5614,15 +5703,44 @@ fn dirRenameWindows(...@@ -5614,15 +5703,44 @@ fn dirRenameWindows(
5614 new_dir: Dir,5703 new_dir: Dir,
5615 new_sub_path: []const u8,5704 new_sub_path: []const u8,
5616) Dir.RenameError!void {5705) Dir.RenameError!void {
5617 const w = windows;
5618 const t: *Threaded = @ptrCast(@alignCast(userdata));5706 const t: *Threaded = @ptrCast(@alignCast(userdata));
5619 _ = t;5707 _ = t;
5708 return dirRenameWindowsInner(old_dir, old_sub_path, new_dir, new_sub_path, true) catch |err| switch (err) {
5709 error.PathAlreadyExists => return error.Unexpected,
5710 error.OperationUnsupported => return error.Unexpected,
5711 else => |e| return e,
5712 };
5713}
56205714
5715fn dirRenamePreserve(
5716 userdata: ?*anyopaque,
5717 old_dir: Dir,
5718 old_sub_path: []const u8,
5719 new_dir: Dir,
5720 new_sub_path: []const u8,
5721) Dir.RenamePreserveError!void {
5722 const t: *Threaded = @ptrCast(@alignCast(userdata));
5723 if (is_windows) return dirRenameWindowsInner(old_dir, old_sub_path, new_dir, new_sub_path, false);
5724 if (native_os == .linux) return dirRenamePreserveLinux(old_dir, old_sub_path, new_dir, new_sub_path);
5725 // Make a hard link then delete the original.
5726 try dirHardLink(t, old_dir, old_sub_path, new_dir, new_sub_path, .{ .follow_symlinks = false });
5727 const prev = swapCancelProtection(t, .blocked);
5728 defer _ = swapCancelProtection(t, prev);
5729 dirDeleteFile(t, old_dir, old_sub_path) catch {};
5730}
5731
5732fn dirRenameWindowsInner(
5733 old_dir: Dir,
5734 old_sub_path: []const u8,
5735 new_dir: Dir,
5736 new_sub_path: []const u8,
5737 replace_if_exists: bool,
5738) Dir.RenamePreserveError!void {
5739 const w = windows;
5621 const old_path_w_buf = try windows.sliceToPrefixedFileW(old_dir.handle, old_sub_path);5740 const old_path_w_buf = try windows.sliceToPrefixedFileW(old_dir.handle, old_sub_path);
5622 const old_path_w = old_path_w_buf.span();5741 const old_path_w = old_path_w_buf.span();
5623 const new_path_w_buf = try windows.sliceToPrefixedFileW(new_dir.handle, new_sub_path);5742 const new_path_w_buf = try windows.sliceToPrefixedFileW(new_dir.handle, new_sub_path);
5624 const new_path_w = new_path_w_buf.span();5743 const new_path_w = new_path_w_buf.span();
5625 const replace_if_exists = true;
56265744
5627 const src_fd = src_fd: {5745 const src_fd = src_fd: {
5628 const syscall: Syscall = try .start();5746 const syscall: Syscall = try .start();
...@@ -5724,9 +5842,9 @@ fn dirRenameWindows(...@@ -5724,9 +5842,9 @@ fn dirRenameWindows(
5724 .ACCESS_DENIED => return error.AccessDenied,5842 .ACCESS_DENIED => return error.AccessDenied,
5725 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,5843 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
5726 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,5844 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
5727 .NOT_SAME_DEVICE => return error.RenameAcrossMountPoints,5845 .NOT_SAME_DEVICE => return error.CrossDevice,
5728 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,5846 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
5729 .DIRECTORY_NOT_EMPTY => return error.PathAlreadyExists,5847 .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
5730 .FILE_IS_A_DIRECTORY => return error.IsDir,5848 .FILE_IS_A_DIRECTORY => return error.IsDir,
5731 .NOT_A_DIRECTORY => return error.NotDir,5849 .NOT_A_DIRECTORY => return error.NotDir,
5732 else => return w.unexpectedStatus(rc),5850 else => return w.unexpectedStatus(rc),
...@@ -5770,10 +5888,10 @@ fn dirRenameWasi(...@@ -5770,10 +5888,10 @@ fn dirRenameWasi(
5770 .NOTDIR => return error.NotDir,5888 .NOTDIR => return error.NotDir,
5771 .NOMEM => return error.SystemResources,5889 .NOMEM => return error.SystemResources,
5772 .NOSPC => return error.NoSpaceLeft,5890 .NOSPC => return error.NoSpaceLeft,
5773 .EXIST => return error.PathAlreadyExists,5891 .EXIST => return error.DirNotEmpty,
5774 .NOTEMPTY => return error.PathAlreadyExists,5892 .NOTEMPTY => return error.DirNotEmpty,
5775 .ROFS => return error.ReadOnlyFileSystem,5893 .ROFS => return error.ReadOnlyFileSystem,
5776 .XDEV => return error.RenameAcrossMountPoints,5894 .XDEV => return error.CrossDevice,
5777 .NOTCAPABLE => return error.AccessDenied,5895 .NOTCAPABLE => return error.AccessDenied,
5778 .ILSEQ => return error.BadPathName,5896 .ILSEQ => return error.BadPathName,
5779 else => |err| return posix.unexpectedErrno(err),5897 else => |err| return posix.unexpectedErrno(err),
...@@ -5799,9 +5917,105 @@ fn dirRenamePosix(...@@ -5799,9 +5917,105 @@ fn dirRenamePosix(
5799 const old_sub_path_posix = try pathToPosix(old_sub_path, &old_path_buffer);5917 const old_sub_path_posix = try pathToPosix(old_sub_path, &old_path_buffer);
5800 const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);5918 const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);
58015919
5920 return renameat(old_dir.handle, old_sub_path_posix, new_dir.handle, new_sub_path_posix);
5921}
5922
5923fn dirRenamePreserveLinux(
5924 old_dir: Dir,
5925 old_sub_path: []const u8,
5926 new_dir: Dir,
5927 new_sub_path: []const u8,
5928) Dir.RenamePreserveError!void {
5929 const linux = std.os.linux;
5930
5931 var old_path_buffer: [linux.PATH_MAX]u8 = undefined;
5932 var new_path_buffer: [linux.PATH_MAX]u8 = undefined;
5933
5934 const old_sub_path_posix = try pathToPosix(old_sub_path, &old_path_buffer);
5935 const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);
5936
5937 const syscall: Syscall = try .start();
5938 while (true) switch (linux.errno(linux.renameat2(
5939 old_dir.handle,
5940 old_sub_path_posix,
5941 new_dir.handle,
5942 new_sub_path_posix,
5943 .{ .NOREPLACE = true },
5944 ))) {
5945 .SUCCESS => return syscall.finish(),
5946 .INTR => {
5947 try syscall.checkCancel();
5948 continue;
5949 },
5950 .ACCES => return syscall.fail(error.AccessDenied),
5951 .PERM => return syscall.fail(error.PermissionDenied),
5952 .BUSY => return syscall.fail(error.FileBusy),
5953 .DQUOT => return syscall.fail(error.DiskQuota),
5954 .ISDIR => return syscall.fail(error.IsDir),
5955 .LOOP => return syscall.fail(error.SymLinkLoop),
5956 .MLINK => return syscall.fail(error.LinkQuotaExceeded),
5957 .NAMETOOLONG => return syscall.fail(error.NameTooLong),
5958 .NOENT => return syscall.fail(error.FileNotFound),
5959 .NOTDIR => return syscall.fail(error.NotDir),
5960 .NOMEM => return syscall.fail(error.SystemResources),
5961 .NOSPC => return syscall.fail(error.NoSpaceLeft),
5962 .EXIST => return syscall.fail(error.PathAlreadyExists),
5963 .NOTEMPTY => return syscall.fail(error.DirNotEmpty),
5964 .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
5965 .XDEV => return syscall.fail(error.CrossDevice),
5966 .ILSEQ => return syscall.fail(error.BadPathName),
5967 .FAULT => |err| return syscall.errnoBug(err),
5968 .INVAL => |err| return syscall.errnoBug(err),
5969 else => |err| return syscall.unexpectedErrno(err),
5970 };
5971}
5972
5973fn renameat(
5974 old_dir: posix.fd_t,
5975 old_sub_path: [*:0]const u8,
5976 new_dir: posix.fd_t,
5977 new_sub_path: [*:0]const u8,
5978) Dir.RenameError!void {
5979 const syscall: Syscall = try .start();
5980 while (true) switch (posix.errno(posix.system.renameat(old_dir, old_sub_path, new_dir, new_sub_path))) {
5981 .SUCCESS => return syscall.finish(),
5982 .INTR => {
5983 try syscall.checkCancel();
5984 continue;
5985 },
5986 .ACCES => return syscall.fail(error.AccessDenied),
5987 .PERM => return syscall.fail(error.PermissionDenied),
5988 .BUSY => return syscall.fail(error.FileBusy),
5989 .DQUOT => return syscall.fail(error.DiskQuota),
5990 .ISDIR => return syscall.fail(error.IsDir),
5991 .IO => return syscall.fail(error.HardwareFailure),
5992 .LOOP => return syscall.fail(error.SymLinkLoop),
5993 .MLINK => return syscall.fail(error.LinkQuotaExceeded),
5994 .NAMETOOLONG => return syscall.fail(error.NameTooLong),
5995 .NOENT => return syscall.fail(error.FileNotFound),
5996 .NOTDIR => return syscall.fail(error.NotDir),
5997 .NOMEM => return syscall.fail(error.SystemResources),
5998 .NOSPC => return syscall.fail(error.NoSpaceLeft),
5999 .EXIST => return syscall.fail(error.DirNotEmpty),
6000 .NOTEMPTY => return syscall.fail(error.DirNotEmpty),
6001 .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
6002 .XDEV => return syscall.fail(error.CrossDevice),
6003 .ILSEQ => return syscall.fail(error.BadPathName),
6004 .FAULT => |err| return syscall.errnoBug(err),
6005 .INVAL => |err| return syscall.errnoBug(err),
6006 else => |err| return syscall.unexpectedErrno(err),
6007 };
6008}
6009
6010fn renameatPreserve(
6011 old_dir: posix.fd_t,
6012 old_sub_path: [*:0]const u8,
6013 new_dir: posix.fd_t,
6014 new_sub_path: [*:0]const u8,
6015) Dir.RenameError!void {
5802 const syscall: Syscall = try .start();6016 const syscall: Syscall = try .start();
5803 while (true) {6017 while (true) {
5804 switch (posix.errno(posix.system.renameat(old_dir.handle, old_sub_path_posix, new_dir.handle, new_sub_path_posix))) {6018 switch (posix.errno(posix.system.renameat(old_dir, old_sub_path, new_dir, new_sub_path))) {
5805 .SUCCESS => return syscall.finish(),6019 .SUCCESS => return syscall.finish(),
5806 .INTR => {6020 .INTR => {
5807 try syscall.checkCancel();6021 try syscall.checkCancel();
...@@ -5827,7 +6041,7 @@ fn dirRenamePosix(...@@ -5827,7 +6041,7 @@ fn dirRenamePosix(
5827 .EXIST => return error.PathAlreadyExists,6041 .EXIST => return error.PathAlreadyExists,
5828 .NOTEMPTY => return error.PathAlreadyExists,6042 .NOTEMPTY => return error.PathAlreadyExists,
5829 .ROFS => return error.ReadOnlyFileSystem,6043 .ROFS => return error.ReadOnlyFileSystem,
5830 .XDEV => return error.RenameAcrossMountPoints,6044 .XDEV => return error.CrossDevice,
5831 .ILSEQ => return error.BadPathName,6045 .ILSEQ => return error.BadPathName,
5832 else => |err| return posix.unexpectedErrno(err),6046 else => |err| return posix.unexpectedErrno(err),
5833 }6047 }
...@@ -6318,11 +6532,6 @@ fn fchmodatFallback(...@@ -6318,11 +6532,6 @@ fn fchmodatFallback(
6318 mode: posix.mode_t,6532 mode: posix.mode_t,
6319) Dir.SetFilePermissionsError!void {6533) Dir.SetFilePermissionsError!void {
6320 comptime assert(native_os == .linux);6534 comptime assert(native_os == .linux);
6321 const use_c = std.c.versionCheck(if (builtin.abi.isAndroid())
6322 .{ .major = 30, .minor = 0, .patch = 0 }
6323 else
6324 .{ .major = 2, .minor = 28, .patch = 0 });
6325 const sys = if (use_c) std.c else std.os.linux;
63266535
6327 // Fallback to changing permissions using procfs:6536 // Fallback to changing permissions using procfs:
6328 //6537 //
...@@ -6369,6 +6578,7 @@ fn fchmodatFallback(...@@ -6369,6 +6578,7 @@ fn fchmodatFallback(
6369 defer posix.close(path_fd);6578 defer posix.close(path_fd);
63706579
6371 const path_mode = mode: {6580 const path_mode = mode: {
6581 const sys = if (statx_use_c) std.c else std.os.linux;
6372 const syscall: Syscall = try .start();6582 const syscall: Syscall = try .start();
6373 while (true) {6583 while (true) {
6374 var statx = std.mem.zeroes(std.os.linux.Statx);6584 var statx = std.mem.zeroes(std.os.linux.Statx);
...@@ -7612,7 +7822,7 @@ fn dirHardLink(...@@ -7612,7 +7822,7 @@ fn dirHardLink(
7612 .NOTDIR => return error.NotDir,7822 .NOTDIR => return error.NotDir,
7613 .PERM => return error.PermissionDenied,7823 .PERM => return error.PermissionDenied,
7614 .ROFS => return error.ReadOnlyFileSystem,7824 .ROFS => return error.ReadOnlyFileSystem,
7615 .XDEV => return error.NotSameFileSystem,7825 .XDEV => return error.CrossDevice,
7616 .INVAL => |err| return errnoBug(err),7826 .INVAL => |err| return errnoBug(err),
7617 .ILSEQ => return error.BadPathName,7827 .ILSEQ => return error.BadPathName,
7618 else => |err| return posix.unexpectedErrno(err),7828 else => |err| return posix.unexpectedErrno(err),
...@@ -7628,7 +7838,7 @@ fn dirHardLink(...@@ -7628,7 +7838,7 @@ fn dirHardLink(
7628 const old_sub_path_posix = try pathToPosix(old_sub_path, &old_path_buffer);7838 const old_sub_path_posix = try pathToPosix(old_sub_path, &old_path_buffer);
7629 const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);7839 const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);
76307840
7631 const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;7841 const flags: u32 = if (options.follow_symlinks) posix.AT.SYMLINK_FOLLOW else 0;
7632 return linkat(old_dir.handle, old_sub_path_posix, new_dir.handle, new_sub_path_posix, flags);7842 return linkat(old_dir.handle, old_sub_path_posix, new_dir.handle, new_sub_path_posix, flags);
7633}7843}
76347844
...@@ -12268,16 +12478,7 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat {...@@ -12268,16 +12478,7 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat {
12268 .nlink = stx.nlink,12478 .nlink = stx.nlink,
12269 .size = stx.size,12479 .size = stx.size,
12270 .permissions = .fromMode(stx.mode),12480 .permissions = .fromMode(stx.mode),
12271 .kind = switch (stx.mode & std.os.linux.S.IFMT) {12481 .kind = statxKind(stx.mode),
12272 std.os.linux.S.IFDIR => .directory,
12273 std.os.linux.S.IFCHR => .character_device,
12274 std.os.linux.S.IFBLK => .block_device,
12275 std.os.linux.S.IFREG => .file,
12276 std.os.linux.S.IFIFO => .named_pipe,
12277 std.os.linux.S.IFLNK => .sym_link,
12278 std.os.linux.S.IFSOCK => .unix_domain_socket,
12279 else => .unknown,
12280 },
12281 .atime = if (!stx.mask.ATIME) null else .{12482 .atime = if (!stx.mask.ATIME) null else .{
12282 .nanoseconds = @intCast(@as(i128, stx.atime.sec) * std.time.ns_per_s + stx.atime.nsec),12483 .nanoseconds = @intCast(@as(i128, stx.atime.sec) * std.time.ns_per_s + stx.atime.nsec),
12283 },12484 },
...@@ -12286,6 +12487,19 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat {...@@ -12286,6 +12487,19 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat {
12286 };12487 };
12287}12488}
1228812489
12490fn statxKind(stx_mode: u16) File.Kind {
12491 return switch (stx_mode & std.os.linux.S.IFMT) {
12492 std.os.linux.S.IFDIR => .directory,
12493 std.os.linux.S.IFCHR => .character_device,
12494 std.os.linux.S.IFBLK => .block_device,
12495 std.os.linux.S.IFREG => .file,
12496 std.os.linux.S.IFIFO => .named_pipe,
12497 std.os.linux.S.IFLNK => .sym_link,
12498 std.os.linux.S.IFSOCK => .unix_domain_socket,
12499 else => .unknown,
12500 };
12501}
12502
12289fn statFromPosix(st: *const posix.Stat) File.Stat {12503fn statFromPosix(st: *const posix.Stat) File.Stat {
12290 const atime = st.atime();12504 const atime = st.atime();
12291 const mtime = st.mtime();12505 const mtime = st.mtime();
...@@ -12441,7 +12655,8 @@ fn lookupDns(...@@ -12441,7 +12655,8 @@ fn lookupDns(
1244112655
12442 for (family_records) |fr| {12656 for (family_records) |fr| {
12443 if (options.family != fr.af) {12657 if (options.family != fr.af) {
12444 const entropy = std.crypto.random.array(u8, 2);12658 var entropy: [2]u8 = undefined;
12659 random(t, &entropy);
12445 const len = writeResolutionQuery(&query_buffers[nq], 0, lookup_canon_name, 1, fr.rr, entropy);12660 const len = writeResolutionQuery(&query_buffers[nq], 0, lookup_canon_name, 1, fr.rr, entropy);
12446 queries_buffer[nq] = query_buffers[nq][0..len];12661 queries_buffer[nq] = query_buffers[nq][0..len];
12447 nq += 1;12662 nq += 1;
...@@ -13853,6 +14068,62 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro...@@ -13853,6 +14068,62 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
13853 };14068 };
13854}14069}
1385514070
14071fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
14072 {
14073 t.mutex.lock();
14074 defer t.mutex.unlock();
14075 if (t.random_file.handle) |handle| return handle;
14076 }
14077
14078 const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' };
14079
14080 var nt_name: windows.UNICODE_STRING = .{
14081 .Length = device_path.len * 2,
14082 .MaximumLength = 0,
14083 .Buffer = @constCast(&device_path),
14084 };
14085 var fresh_handle: windows.HANDLE = undefined;
14086 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
14087 var syscall: Syscall = try .start();
14088 while (true) switch (windows.ntdll.NtOpenFile(
14089 &fresh_handle,
14090 .{
14091 .STANDARD = .{ .SYNCHRONIZE = true },
14092 .SPECIFIC = .{ .FILE = .{ .READ_DATA = true } },
14093 },
14094 &.{
14095 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
14096 .RootDirectory = null,
14097 .ObjectName = &nt_name,
14098 .Attributes = .{},
14099 .SecurityDescriptor = null,
14100 .SecurityQualityOfService = null,
14101 },
14102 &io_status_block,
14103 .VALID_FLAGS,
14104 .{ .IO = .SYNCHRONOUS_NONALERT },
14105 )) {
14106 .SUCCESS => {
14107 syscall.finish();
14108 t.mutex.lock(); // Another thread might have won the race.
14109 defer t.mutex.unlock();
14110 if (t.random_file.handle) |prev_handle| {
14111 _ = windows.ntdll.NtClose(fresh_handle);
14112 return prev_handle;
14113 } else {
14114 t.random_file.handle = fresh_handle;
14115 return fresh_handle;
14116 }
14117 },
14118 .CANCELLED => {
14119 try syscall.checkCancel();
14120 continue;
14121 },
14122 .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.EntropyUnavailable), // Observed on wine 10.0
14123 else => return syscall.fail(error.EntropyUnavailable),
14124 };
14125}
14126
13856fn getNulHandle(t: *Threaded) !windows.HANDLE {14127fn getNulHandle(t: *Threaded) !windows.HANDLE {
13857 {14128 {
13858 t.mutex.lock();14129 t.mutex.lock();
...@@ -14935,6 +15206,305 @@ pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 {...@@ -14935,6 +15206,305 @@ pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 {
14935 return @field(t.environ.string, name);15206 return @field(t.environ.string, name);
14936}15207}
1493715208
15209fn random(userdata: ?*anyopaque, buffer: []u8) void {
15210 const t: *Threaded = @ptrCast(@alignCast(userdata));
15211 const thread = Thread.current orelse return randomMainThread(t, buffer);
15212 if (!thread.csprng.isInitialized()) {
15213 @branchHint(.unlikely);
15214 var seed: [Csprng.seed_len]u8 = undefined;
15215 randomMainThread(t, &seed);
15216 thread.csprng.rng = .init(seed);
15217 }
15218 thread.csprng.rng.fill(buffer);
15219}
15220
15221fn randomMainThread(t: *Threaded, buffer: []u8) void {
15222 t.mutex.lock();
15223 defer t.mutex.unlock();
15224
15225 if (!t.csprng.isInitialized()) {
15226 @branchHint(.unlikely);
15227 var seed: [Csprng.seed_len]u8 = undefined;
15228 {
15229 t.mutex.unlock();
15230 defer t.mutex.lock();
15231
15232 const prev = swapCancelProtection(t, .blocked);
15233 defer _ = swapCancelProtection(t, prev);
15234
15235 randomSecure(t, &seed) catch |err| switch (err) {
15236 error.Canceled => unreachable,
15237 error.EntropyUnavailable => {
15238 @memset(&seed, 0);
15239 const aslr_addr = @intFromPtr(t);
15240 std.mem.writeInt(usize, seed[seed.len - @sizeOf(usize) ..][0..@sizeOf(usize)], aslr_addr, .native);
15241 switch (native_os) {
15242 .windows => fallbackSeedWindows(&seed),
15243 .wasi => if (builtin.link_libc) fallbackSeedPosix(&seed) else fallbackSeedWasi(&seed),
15244 else => fallbackSeedPosix(&seed),
15245 }
15246 },
15247 };
15248 }
15249 t.csprng.rng = .init(seed);
15250 }
15251
15252 t.csprng.rng.fill(buffer);
15253}
15254
15255fn fallbackSeedPosix(seed: *[Csprng.seed_len]u8) void {
15256 std.mem.writeInt(posix.pid_t, seed[0..@sizeOf(posix.pid_t)], posix.system.getpid(), .native);
15257 const i_1 = @sizeOf(posix.pid_t);
15258
15259 var ts: posix.timespec = undefined;
15260 const Sec = @TypeOf(ts.sec);
15261 const Nsec = @TypeOf(ts.nsec);
15262 const i_2 = i_1 + @sizeOf(Sec);
15263 switch (posix.errno(posix.system.clock_gettime(.REALTIME, &ts))) {
15264 .SUCCESS => {
15265 std.mem.writeInt(Sec, seed[i_1..][0..@sizeOf(Sec)], ts.sec, .native);
15266 std.mem.writeInt(Nsec, seed[i_2..][0..@sizeOf(Nsec)], ts.nsec, .native);
15267 },
15268 else => {},
15269 }
15270}
15271
15272fn fallbackSeedWindows(seed: *[Csprng.seed_len]u8) void {
15273 var pc: windows.LARGE_INTEGER = undefined;
15274 _ = windows.ntdll.RtlQueryPerformanceCounter(&pc);
15275 std.mem.writeInt(windows.LARGE_INTEGER, seed[0..@sizeOf(windows.LARGE_INTEGER)], pc, .native);
15276}
15277
15278fn fallbackSeedWasi(seed: *[Csprng.seed_len]u8) void {
15279 var ts: std.os.wasi.timestamp_t = undefined;
15280 if (std.os.wasi.clock_time_get(.REALTIME, 1, &ts) == .SUCCESS) {
15281 std.mem.writeInt(std.os.wasi.timestamp_t, seed[0..@sizeOf(std.os.wasi.timestamp_t)], ts, .native);
15282 }
15283}
15284
15285fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void {
15286 const t: *Threaded = @ptrCast(@alignCast(userdata));
15287
15288 if (is_windows) {
15289 if (buffer.len == 0) return;
15290 // ProcessPrng from bcryptprimitives.dll has the following properties:
15291 // * introduces a dependency on bcryptprimitives.dll, which apparently
15292 // runs a test suite every time it is loaded
15293 // * heap allocates a 48-byte buffer, handling failure by returning NO_MEMORY in a BOOL
15294 // despite the function being documented to always return TRUE
15295 // * reads from "\\Device\\CNG" which then seeds a per-CPU AES CSPRNG
15296 // Therefore, that function is avoided in favor of using the device directly.
15297 const cng_device = try getCngHandle(t);
15298 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
15299 var i: usize = 0;
15300 const syscall: Syscall = try .start();
15301 while (true) {
15302 const remaining_len = std.math.lossyCast(u32, buffer.len - i);
15303 switch (windows.ntdll.NtDeviceIoControlFile(
15304 cng_device,
15305 null,
15306 null,
15307 null,
15308 &io_status_block,
15309 windows.IOCTL.KSEC.GEN_RANDOM,
15310 null,
15311 0,
15312 buffer[i..].ptr,
15313 remaining_len,
15314 )) {
15315 .SUCCESS => {
15316 i += remaining_len;
15317 if (buffer.len - i == 0) {
15318 return syscall.finish();
15319 } else {
15320 try syscall.checkCancel();
15321 continue;
15322 }
15323 },
15324 .CANCELLED => {
15325 try syscall.checkCancel();
15326 continue;
15327 },
15328 else => return syscall.fail(error.EntropyUnavailable),
15329 }
15330 }
15331 }
15332
15333 if (builtin.link_libc and @TypeOf(posix.system.arc4random_buf) != void) {
15334 if (buffer.len == 0) return;
15335 posix.system.arc4random_buf(buffer.ptr, buffer.len);
15336 return;
15337 }
15338
15339 if (native_os == .wasi) {
15340 if (buffer.len == 0) return;
15341 const syscall: Syscall = try .start();
15342 while (true) switch (std.os.wasi.random_get(buffer.ptr, buffer.len)) {
15343 .SUCCESS => return syscall.finish(),
15344 .INTR => {
15345 try syscall.checkCancel();
15346 continue;
15347 },
15348 else => return syscall.fail(error.EntropyUnavailable),
15349 };
15350 }
15351
15352 if (@TypeOf(posix.system.getrandom) != void) {
15353 const getrandom = if (use_libc_getrandom) std.c.getrandom else std.os.linux.getrandom;
15354 var i: usize = 0;
15355 const syscall: Syscall = try .start();
15356 while (buffer.len - i != 0) {
15357 const buf = buffer[i..];
15358 const rc = getrandom(buf.ptr, buf.len, 0);
15359 switch (posix.errno(rc)) {
15360 .SUCCESS => {
15361 syscall.finish();
15362 const n: usize = @intCast(rc);
15363 i += n;
15364 continue;
15365 },
15366 .INTR => {
15367 try syscall.checkCancel();
15368 continue;
15369 },
15370 else => return syscall.fail(error.EntropyUnavailable),
15371 }
15372 }
15373 return;
15374 }
15375
15376 if (native_os == .emscripten) {
15377 if (buffer.len == 0) return;
15378 const err = posix.errno(std.c.getentropy(buffer.ptr, buffer.len));
15379 switch (err) {
15380 .SUCCESS => return,
15381 else => return error.EntropyUnavailable,
15382 }
15383 }
15384
15385 if (native_os == .linux) {
15386 comptime assert(use_dev_urandom);
15387 const urandom_fd = try getRandomFd(t);
15388
15389 var i: usize = 0;
15390 while (buffer.len - i != 0) {
15391 const syscall: Syscall = try .start();
15392 const rc = posix.system.read(urandom_fd, buffer[i..].ptr, buffer.len - i);
15393 switch (posix.errno(rc)) {
15394 .SUCCESS => {
15395 syscall.finish();
15396 const n: usize = @intCast(rc);
15397 if (n == 0) return error.EntropyUnavailable;
15398 i += n;
15399 continue;
15400 },
15401 .INTR => {
15402 try syscall.checkCancel();
15403 continue;
15404 },
15405 else => return syscall.fail(error.EntropyUnavailable),
15406 }
15407 }
15408 }
15409
15410 return error.EntropyUnavailable;
15411}
15412
15413fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
15414 {
15415 t.mutex.lock();
15416 defer t.mutex.unlock();
15417
15418 if (t.random_file.fd == -2) return error.EntropyUnavailable;
15419 if (t.random_file.fd != -1) return t.random_file.fd;
15420 }
15421
15422 const mode: posix.mode_t = 0;
15423
15424 const fd: posix.fd_t = fd: {
15425 const syscall: Syscall = try .start();
15426 while (true) {
15427 const rc = openat_sym(posix.AT.FDCWD, "/dev/urandom", .{
15428 .ACCMODE = .RDONLY,
15429 .CLOEXEC = true,
15430 }, mode);
15431 switch (posix.errno(rc)) {
15432 .SUCCESS => {
15433 syscall.finish();
15434 break :fd @intCast(rc);
15435 },
15436 .INTR => {
15437 try syscall.checkCancel();
15438 continue;
15439 },
15440 else => return syscall.fail(error.EntropyUnavailable),
15441 }
15442 }
15443 };
15444 errdefer posix.close(fd);
15445
15446 switch (native_os) {
15447 .linux => {
15448 const sys = if (statx_use_c) std.c else std.os.linux;
15449 const syscall: Syscall = try .start();
15450 while (true) {
15451 var statx = std.mem.zeroes(std.os.linux.Statx);
15452 switch (sys.errno(sys.statx(fd, "", std.os.linux.AT.EMPTY_PATH, .{ .TYPE = true }, &statx))) {
15453 .SUCCESS => {
15454 syscall.finish();
15455 if (!statx.mask.TYPE) return error.EntropyUnavailable;
15456 t.mutex.lock(); // Another thread might have won the race.
15457 defer t.mutex.unlock();
15458 if (t.random_file.fd >= 0) {
15459 posix.close(fd);
15460 return t.random_file.fd;
15461 } else if (!posix.S.ISCHR(statx.mode)) {
15462 t.random_file.fd = -2;
15463 return error.EntropyUnavailable;
15464 } else {
15465 t.random_file.fd = fd;
15466 return fd;
15467 }
15468 },
15469 .INTR => {
15470 try syscall.checkCancel();
15471 continue;
15472 },
15473 else => return syscall.fail(error.EntropyUnavailable),
15474 }
15475 }
15476 },
15477 else => {
15478 const syscall: Syscall = try .start();
15479 while (true) {
15480 var stat = std.mem.zeroes(posix.Stat);
15481 switch (posix.errno(fstat_sym(fd, &stat))) {
15482 .SUCCESS => {
15483 syscall.finish();
15484 t.mutex.lock(); // Another thread might have won the race.
15485 defer t.mutex.unlock();
15486 if (t.random_file.fd >= 0) {
15487 posix.close(fd);
15488 return t.random_file.fd;
15489 } else if (!posix.S.ISCHR(stat.mode)) {
15490 t.random_file.fd = -2;
15491 return error.EntropyUnavailable;
15492 } else {
15493 t.random_file.fd = fd;
15494 return fd;
15495 }
15496 },
15497 .INTR => {
15498 try syscall.checkCancel();
15499 continue;
15500 },
15501 else => return syscall.fail(error.EntropyUnavailable),
15502 }
15503 }
15504 },
15505 }
15506}
15507
14938test {15508test {
14939 _ = @import("Threaded/test.zig");15509 _ = @import("Threaded/test.zig");
14940}15510}
lib/std/Io/net/test.zig+3-3
...@@ -275,7 +275,7 @@ test "listen on a unix socket, send bytes, receive bytes" {...@@ -275,7 +275,7 @@ test "listen on a unix socket, send bytes, receive bytes" {
275275
276 const io = testing.io;276 const io = testing.io;
277277
278 const socket_path = try generateFileName("socket.unix");278 const socket_path = try generateFileName(io, "socket.unix");
279 defer testing.allocator.free(socket_path);279 defer testing.allocator.free(socket_path);
280280
281 const socket_addr = try net.UnixAddress.init(socket_path);281 const socket_addr = try net.UnixAddress.init(socket_path);
...@@ -308,11 +308,11 @@ test "listen on a unix socket, send bytes, receive bytes" {...@@ -308,11 +308,11 @@ test "listen on a unix socket, send bytes, receive bytes" {
308 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);308 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);
309}309}
310310
311fn generateFileName(base_name: []const u8) ![]const u8 {311fn generateFileName(io: Io, base_name: []const u8) ![]const u8 {
312 const random_bytes_count = 12;312 const random_bytes_count = 12;
313 const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);313 const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);
314 var random_bytes: [12]u8 = undefined;314 var random_bytes: [12]u8 = undefined;
315 std.crypto.random.bytes(&random_bytes);315 io.random(&random_bytes);
316 var sub_path: [sub_path_len]u8 = undefined;316 var sub_path: [sub_path_len]u8 = undefined;
317 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);317 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
318 return std.fmt.allocPrint(testing.allocator, "{s}-{s}", .{ sub_path[0..], base_name });318 return std.fmt.allocPrint(testing.allocator, "{s}-{s}", .{ sub_path[0..], base_name });
lib/std/Io/test.zig+26
...@@ -564,3 +564,29 @@ test "tasks spawned in group after Group.cancel are canceled" {...@@ -564,3 +564,29 @@ test "tasks spawned in group after Group.cancel are canceled" {
564 try io.sleep(.fromMilliseconds(10), .awake); // let that first sleep start up564 try io.sleep(.fromMilliseconds(10), .awake); // let that first sleep start up
565 try group.concurrent(io, global.waitThenSpawn, .{ io, &group });565 try group.concurrent(io, global.waitThenSpawn, .{ io, &group });
566}566}
567
568test "random" {
569 const io = testing.io;
570
571 var a: u64 = undefined;
572 var b: u64 = undefined;
573 var c: u64 = undefined;
574
575 io.random(@ptrCast(&a));
576 io.random(@ptrCast(&b));
577 io.random(@ptrCast(&c));
578
579 try std.testing.expect(a ^ b ^ c != 0);
580}
581
582test "randomSecure" {
583 const io = testing.io;
584
585 var buf_a: [50]u8 = undefined;
586 var buf_b: [50]u8 = undefined;
587 try io.randomSecure(&buf_a);
588 try io.randomSecure(&buf_b);
589 // If this test fails the chance is significantly higher that there is a bug than
590 // that two sets of 50 bytes were equal.
591 try expect(!mem.eql(u8, &buf_a, &buf_b));
592}
lib/std/Random.zig+17-3
...@@ -1,15 +1,13 @@...@@ -1,15 +1,13 @@
1//! The engines provided here should be initialized from an external source.1//! The engines provided here should be initialized from an external source.
2//! For a thread-local cryptographically secure pseudo random number generator,
3//! use `std.crypto.random`.
4//! Be sure to use a CSPRNG when required, otherwise using a normal PRNG will2//! Be sure to use a CSPRNG when required, otherwise using a normal PRNG will
5//! be faster and use substantially less stack space.3//! be faster and use substantially less stack space.
4const Random = @This();
65
7const std = @import("std.zig");6const std = @import("std.zig");
8const math = std.math;7const math = std.math;
9const mem = std.mem;8const mem = std.mem;
10const assert = std.debug.assert;9const assert = std.debug.assert;
11const maxInt = std.math.maxInt;10const maxInt = std.math.maxInt;
12const Random = @This();
1311
14/// Fast unbiased random numbers.12/// Fast unbiased random numbers.
15pub const DefaultPrng = Xoshiro256;13pub const DefaultPrng = Xoshiro256;
...@@ -35,6 +33,22 @@ pub const ziggurat = @import("Random/ziggurat.zig");...@@ -35,6 +33,22 @@ pub const ziggurat = @import("Random/ziggurat.zig");
35ptr: *anyopaque,33ptr: *anyopaque,
36fillFn: *const fn (ptr: *anyopaque, buf: []u8) void,34fillFn: *const fn (ptr: *anyopaque, buf: []u8) void,
3735
36pub const IoSource = struct {
37 io: std.Io,
38
39 pub fn interface(this: *const @This()) std.Random {
40 return .{
41 .ptr = @constCast(this),
42 .fillFn = fill,
43 };
44 }
45
46 fn fill(ptr: *anyopaque, buffer: []u8) void {
47 const this: *const @This() = @ptrCast(@alignCast(ptr));
48 this.io.random(buffer);
49 }
50};
51
38pub fn init(pointer: anytype, comptime fillFn: fn (ptr: @TypeOf(pointer), buf: []u8) void) Random {52pub fn init(pointer: anytype, comptime fillFn: fn (ptr: @TypeOf(pointer), buf: []u8) void) Random {
39 const Ptr = @TypeOf(pointer);53 const Ptr = @TypeOf(pointer);
40 assert(@typeInfo(Ptr) == .pointer); // Must be a pointer54 assert(@typeInfo(Ptr) == .pointer); // Must be a pointer
lib/std/Random/ChaCha.zig+1-1
...@@ -20,7 +20,7 @@ pub const secret_seed_length = Cipher.key_length;...@@ -20,7 +20,7 @@ pub const secret_seed_length = Cipher.key_length;
2020
21/// The seed must be uniform, secret and `secret_seed_length` bytes long.21/// The seed must be uniform, secret and `secret_seed_length` bytes long.
22pub fn init(secret_seed: [secret_seed_length]u8) Self {22pub fn init(secret_seed: [secret_seed_length]u8) Self {
23 var self = Self{ .state = undefined, .offset = 0 };23 var self: Self = .{ .state = undefined, .offset = 0 };
24 Cipher.stream(&self.state, 0, secret_seed, nonce);24 Cipher.stream(&self.state, 0, secret_seed, nonce);
25 return self;25 return self;
26}26}
lib/std/Random/test.zig+2-1
...@@ -436,8 +436,9 @@ fn testRangeBias(r: Random, start: i8, end: i8, biased: bool) !void {...@@ -436,8 +436,9 @@ fn testRangeBias(r: Random, start: i8, end: i8, biased: bool) !void {
436}436}
437437
438test "CSPRNG" {438test "CSPRNG" {
439 const io = std.testing.io;
439 var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined;440 var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined;
440 std.crypto.random.bytes(&secret_seed);441 io.random(&secret_seed);
441 var csprng = DefaultCsprng.init(secret_seed);442 var csprng = DefaultCsprng.init(secret_seed);
442 const random = csprng.random();443 const random = csprng.random();
443 const a = random.int(u64);444 const a = random.int(u64);
lib/std/crypto.zig+4-11
...@@ -235,9 +235,6 @@ pub const nacl = struct {...@@ -235,9 +235,6 @@ pub const nacl = struct {
235/// Finite-field arithmetic.235/// Finite-field arithmetic.
236pub const ff = @import("crypto/ff.zig");236pub const ff = @import("crypto/ff.zig");
237237
238/// This is a thread-local, cryptographically secure pseudo random number generator.
239pub const random = @import("crypto/tlcsprng.zig").interface;
240
241/// Encoding and decoding238/// Encoding and decoding
242pub const codecs = @import("crypto/codecs.zig");239pub const codecs = @import("crypto/codecs.zig");
243240
...@@ -306,6 +303,9 @@ test {...@@ -306,6 +303,9 @@ test {
306 _ = dh.X25519;303 _ = dh.X25519;
307304
308 _ = kem.kyber_d00;305 _ = kem.kyber_d00;
306 _ = kem.hybrid;
307 _ = kem.kyber_d00;
308 _ = kem.ml_kem;
309309
310 _ = ecc.Curve25519;310 _ = ecc.Curve25519;
311 _ = ecc.Edwards25519;311 _ = ecc.Edwards25519;
...@@ -343,6 +343,7 @@ test {...@@ -343,6 +343,7 @@ test {
343343
344 _ = sign.Ed25519;344 _ = sign.Ed25519;
345 _ = sign.ecdsa;345 _ = sign.ecdsa;
346 _ = sign.mldsa;
346347
347 _ = stream.chacha.ChaCha20IETF;348 _ = stream.chacha.ChaCha20IETF;
348 _ = stream.chacha.ChaCha12IETF;349 _ = stream.chacha.ChaCha12IETF;
...@@ -364,20 +365,12 @@ test {...@@ -364,20 +365,12 @@ test {
364 _ = secureZero;365 _ = secureZero;
365 _ = timing_safe;366 _ = timing_safe;
366 _ = ff;367 _ = ff;
367 _ = random;
368 _ = errors;368 _ = errors;
369 _ = tls;369 _ = tls;
370 _ = Certificate;370 _ = Certificate;
371 _ = codecs;371 _ = codecs;
372}372}
373373
374test "CSPRNG" {
375 const a = random.int(u64);
376 const b = random.int(u64);
377 const c = random.int(u64);
378 try std.testing.expect(a ^ b ^ c != 0);
379}
380
381test "issue #4532: no index out of bounds" {374test "issue #4532: no index out of bounds" {
382 const types = [_]type{375 const types = [_]type{
383 hash.Md5,376 hash.Md5,
lib/std/crypto/25519/ed25519.zig+32-23
...@@ -333,12 +333,10 @@ pub const Ed25519 = struct {...@@ -333,12 +333,10 @@ pub const Ed25519 = struct {
333 }333 }
334334
335 /// Generate a new, random key pair.335 /// Generate a new, random key pair.
336 ///336 pub fn generate(io: std.Io) KeyPair {
337 /// `crypto.random.bytes` must be supported by the target.
338 pub fn generate() KeyPair {
339 var random_seed: [seed_length]u8 = undefined;337 var random_seed: [seed_length]u8 = undefined;
340 while (true) {338 while (true) {
341 crypto.random.bytes(&random_seed);339 io.random(&random_seed);
342 return generateDeterministic(random_seed) catch {340 return generateDeterministic(random_seed) catch {
343 @branchHint(.unlikely);341 @branchHint(.unlikely);
344 continue;342 continue;
...@@ -389,18 +387,21 @@ pub const Ed25519 = struct {...@@ -389,18 +387,21 @@ pub const Ed25519 = struct {
389387
390 /// Create a Signer, that can be used for incremental signing.388 /// Create a Signer, that can be used for incremental signing.
391 /// Note that the signature is not deterministic.389 /// Note that the signature is not deterministic.
392 /// The noise parameter, if set, should be something unique for each message,390 pub fn signer(
393 /// such as a random nonce, or a counter.391 key_pair: KeyPair,
394 pub fn signer(key_pair: KeyPair, noise: ?[noise_length]u8) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer {392 /// If set, should be something unique for each message, such as a
393 /// random nonce, or a counter.
394 noise: ?[noise_length]u8,
395 /// Filled with cryptographically secure randomness.
396 entropy: *const [noise_length]u8,
397 ) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer {
395 if (!mem.eql(u8, &key_pair.secret_key.publicKeyBytes(), &key_pair.public_key.toBytes())) {398 if (!mem.eql(u8, &key_pair.secret_key.publicKeyBytes(), &key_pair.public_key.toBytes())) {
396 return error.KeyMismatch;399 return error.KeyMismatch;
397 }400 }
398 const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix();401 const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix();
399 var h = Sha512.init(.{});402 var h = Sha512.init(.{});
400 h.update(&scalar_and_prefix.prefix);403 h.update(&scalar_and_prefix.prefix);
401 var noise2: [noise_length]u8 = undefined;404 h.update(entropy);
402 crypto.random.bytes(&noise2);
403 h.update(&noise2);
404 if (noise) |*z| {405 if (noise) |*z| {
405 h.update(z);406 h.update(z);
406 }407 }
...@@ -420,7 +421,7 @@ pub const Ed25519 = struct {...@@ -420,7 +421,7 @@ pub const Ed25519 = struct {
420 };421 };
421422
422 /// Verify several signatures in a single operation, much faster than verifying signatures one-by-one423 /// Verify several signatures in a single operation, much faster than verifying signatures one-by-one
423 pub fn verifyBatch(comptime count: usize, signature_batch: [count]BatchElement) (SignatureVerificationError || IdentityElementError || WeakPublicKeyError || EncodingError || NonCanonicalError)!void {424 pub fn verifyBatch(io: std.Io, comptime count: usize, signature_batch: [count]BatchElement) (SignatureVerificationError || IdentityElementError || WeakPublicKeyError || EncodingError || NonCanonicalError)!void {
424 var r_batch: [count]CompressedScalar = undefined;425 var r_batch: [count]CompressedScalar = undefined;
425 var s_batch: [count]CompressedScalar = undefined;426 var s_batch: [count]CompressedScalar = undefined;
426 var a_batch: [count]Curve = undefined;427 var a_batch: [count]Curve = undefined;
...@@ -454,7 +455,7 @@ pub const Ed25519 = struct {...@@ -454,7 +455,7 @@ pub const Ed25519 = struct {
454455
455 var z_batch: [count]Curve.scalar.CompressedScalar = undefined;456 var z_batch: [count]Curve.scalar.CompressedScalar = undefined;
456 for (&z_batch) |*z| {457 for (&z_batch) |*z| {
457 crypto.random.bytes(z[0..16]);458 io.random(z[0..16]);
458 @memset(z[16..], 0);459 @memset(z[16..], 0);
459 }460 }
460461
...@@ -587,12 +588,14 @@ test "signature" {...@@ -587,12 +588,14 @@ test "signature" {
587}588}
588589
589test "batch verification" {590test "batch verification" {
591 const io = std.testing.io;
592
590 for (0..16) |_| {593 for (0..16) |_| {
591 const key_pair = Ed25519.KeyPair.generate();594 const key_pair = Ed25519.KeyPair.generate(io);
592 var msg1: [32]u8 = undefined;595 var msg1: [32]u8 = undefined;
593 var msg2: [32]u8 = undefined;596 var msg2: [32]u8 = undefined;
594 crypto.random.bytes(&msg1);597 io.random(&msg1);
595 crypto.random.bytes(&msg2);598 io.random(&msg2);
596 const sig1 = try key_pair.sign(&msg1, null);599 const sig1 = try key_pair.sign(&msg1, null);
597 const sig2 = try key_pair.sign(&msg2, null);600 const sig2 = try key_pair.sign(&msg2, null);
598 var signature_batch = [_]Ed25519.BatchElement{601 var signature_batch = [_]Ed25519.BatchElement{
...@@ -607,10 +610,10 @@ test "batch verification" {...@@ -607,10 +610,10 @@ test "batch verification" {
607 .public_key = key_pair.public_key,610 .public_key = key_pair.public_key,
608 },611 },
609 };612 };
610 try Ed25519.verifyBatch(2, signature_batch);613 try Ed25519.verifyBatch(io, 2, signature_batch);
611614
612 signature_batch[1].sig = sig1;615 signature_batch[1].sig = sig1;
613 try std.testing.expectError(error.SignatureVerificationFailed, Ed25519.verifyBatch(signature_batch.len, signature_batch));616 try std.testing.expectError(error.SignatureVerificationFailed, Ed25519.verifyBatch(io, signature_batch.len, signature_batch));
614 }617 }
615}618}
616619
...@@ -718,14 +721,15 @@ test "test vectors" {...@@ -718,14 +721,15 @@ test "test vectors" {
718}721}
719722
720test "with blind keys" {723test "with blind keys" {
724 const io = std.testing.io;
721 const BlindKeyPair = Ed25519.key_blinding.BlindKeyPair;725 const BlindKeyPair = Ed25519.key_blinding.BlindKeyPair;
722726
723 // Create a standard Ed25519 key pair727 // Create a standard Ed25519 key pair
724 const kp = Ed25519.KeyPair.generate();728 const kp = Ed25519.KeyPair.generate(io);
725729
726 // Create a random blinding seed730 // Create a random blinding seed
727 var blind: [32]u8 = undefined;731 var blind: [32]u8 = undefined;
728 crypto.random.bytes(&blind);732 io.random(&blind);
729733
730 // Blind the key pair734 // Blind the key pair
731 const blind_kp = try BlindKeyPair.init(kp, blind, "ctx");735 const blind_kp = try BlindKeyPair.init(kp, blind, "ctx");
...@@ -741,9 +745,12 @@ test "with blind keys" {...@@ -741,9 +745,12 @@ test "with blind keys" {
741}745}
742746
743test "signatures with streaming" {747test "signatures with streaming" {
744 const kp = Ed25519.KeyPair.generate();748 const io = std.testing.io;
749 const kp = Ed25519.KeyPair.generate(io);
745750
746 var signer = try kp.signer(null);751 var entropy: [Ed25519.noise_length]u8 = undefined;
752 io.random(&entropy);
753 var signer = try kp.signer(null, &entropy);
747 signer.update("mes");754 signer.update("mes");
748 signer.update("sage");755 signer.update("sage");
749 const sig = signer.finalize();756 const sig = signer.finalize();
...@@ -757,7 +764,8 @@ test "signatures with streaming" {...@@ -757,7 +764,8 @@ test "signatures with streaming" {
757}764}
758765
759test "key pair from secret key" {766test "key pair from secret key" {
760 const kp = Ed25519.KeyPair.generate();767 const io = std.testing.io;
768 const kp = Ed25519.KeyPair.generate(io);
761 const kp2 = try Ed25519.KeyPair.fromSecretKey(kp.secret_key);769 const kp2 = try Ed25519.KeyPair.fromSecretKey(kp.secret_key);
762 try std.testing.expectEqualSlices(u8, &kp.secret_key.toBytes(), &kp2.secret_key.toBytes());770 try std.testing.expectEqualSlices(u8, &kp.secret_key.toBytes(), &kp2.secret_key.toBytes());
763 try std.testing.expectEqualSlices(u8, &kp.public_key.toBytes(), &kp2.public_key.toBytes());771 try std.testing.expectEqualSlices(u8, &kp.public_key.toBytes(), &kp2.public_key.toBytes());
...@@ -788,7 +796,8 @@ test "cofactored vs cofactorless verification" {...@@ -788,7 +796,8 @@ test "cofactored vs cofactorless verification" {
788}796}
789797
790test "regular signature verifies with both verify and verifyStrict" {798test "regular signature verifies with both verify and verifyStrict" {
791 const kp = Ed25519.KeyPair.generate();799 const io = std.testing.io;
800 const kp = Ed25519.KeyPair.generate(io);
792 const msg = "test message";801 const msg = "test message";
793 const sig = try kp.sign(msg, null);802 const sig = try kp.sign(msg, null);
794 try sig.verify(msg, kp.public_key);803 try sig.verify(msg, kp.public_key);
lib/std/crypto/25519/edwards25519.zig+5-3
...@@ -575,10 +575,11 @@ test "packing/unpacking" {...@@ -575,10 +575,11 @@ test "packing/unpacking" {
575}575}
576576
577test "point addition/subtraction" {577test "point addition/subtraction" {
578 const io = std.testing.io;
578 var s1: [32]u8 = undefined;579 var s1: [32]u8 = undefined;
579 var s2: [32]u8 = undefined;580 var s2: [32]u8 = undefined;
580 crypto.random.bytes(&s1);581 io.random(&s1);
581 crypto.random.bytes(&s2);582 io.random(&s2);
582 const p = try Edwards25519.basePoint.clampedMul(s1);583 const p = try Edwards25519.basePoint.clampedMul(s1);
583 const q = try Edwards25519.basePoint.clampedMul(s2);584 const q = try Edwards25519.basePoint.clampedMul(s2);
584 const r = p.add(q).add(q).sub(q).sub(q);585 const r = p.add(q).add(q).sub(q).sub(q);
...@@ -622,9 +623,10 @@ test "implicit reduction of invalid scalars" {...@@ -622,9 +623,10 @@ test "implicit reduction of invalid scalars" {
622}623}
623624
624test "subgroup check" {625test "subgroup check" {
626 const io = std.testing.io;
625 for (0..100) |_| {627 for (0..100) |_| {
626 var p = Edwards25519.basePoint;628 var p = Edwards25519.basePoint;
627 const s = Edwards25519.scalar.random();629 const s = Edwards25519.scalar.random(io);
628 p = try p.mulPublic(s);630 p = try p.mulPublic(s);
629 try p.rejectUnexpectedSubgroup();631 try p.rejectUnexpectedSubgroup();
630 }632 }
lib/std/crypto/25519/scalar.zig+7-6
...@@ -101,8 +101,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar) CompressedScalar {...@@ -101,8 +101,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar) CompressedScalar {
101}101}
102102
103/// Return a random scalar < L103/// Return a random scalar < L
104pub fn random() CompressedScalar {104pub fn random(io: std.Io) CompressedScalar {
105 return Scalar.random().toBytes();105 return Scalar.random(io).toBytes();
106}106}
107107
108/// A scalar in unpacked representation108/// A scalar in unpacked representation
...@@ -560,10 +560,10 @@ pub const Scalar = struct {...@@ -560,10 +560,10 @@ pub const Scalar = struct {
560 }560 }
561561
562 /// Return a random scalar < L.562 /// Return a random scalar < L.
563 pub fn random() Scalar {563 pub fn random(io: std.Io) Scalar {
564 var s: [64]u8 = undefined;564 var s: [64]u8 = undefined;
565 while (true) {565 while (true) {
566 crypto.random.bytes(&s);566 io.random(&s);
567 const n = Scalar.fromBytes64(s);567 const n = Scalar.fromBytes64(s);
568 if (!n.isZero()) {568 if (!n.isZero()) {
569 return n;569 return n;
...@@ -879,8 +879,9 @@ test "scalar field inversion" {...@@ -879,8 +879,9 @@ test "scalar field inversion" {
879}879}
880880
881test "random scalar" {881test "random scalar" {
882 const s1 = random();882 const io = std.testing.io;
883 const s2 = random();883 const s1 = random(io);
884 const s2 = random(io);
884 try std.testing.expect(!mem.eql(u8, &s1, &s2));885 try std.testing.expect(!mem.eql(u8, &s1, &s2));
885}886}
886887
lib/std/crypto/25519/x25519.zig+2-2
...@@ -41,10 +41,10 @@ pub const X25519 = struct {...@@ -41,10 +41,10 @@ pub const X25519 = struct {
41 }41 }
4242
43 /// Generate a new, random key pair.43 /// Generate a new, random key pair.
44 pub fn generate() KeyPair {44 pub fn generate(io: std.Io) KeyPair {
45 var random_seed: [seed_length]u8 = undefined;45 var random_seed: [seed_length]u8 = undefined;
46 while (true) {46 while (true) {
47 crypto.random.bytes(&random_seed);47 io.random(&random_seed);
48 return generateDeterministic(random_seed) catch {48 return generateDeterministic(random_seed) catch {
49 @branchHint(.unlikely);49 @branchHint(.unlikely);
50 continue;50 continue;
lib/std/crypto/argon2.zig+1-1
...@@ -533,7 +533,7 @@ const PhcFormatHasher = struct {...@@ -533,7 +533,7 @@ const PhcFormatHasher = struct {
533 if (params.secret != null or params.ad != null) return HasherError.InvalidEncoding;533 if (params.secret != null or params.ad != null) return HasherError.InvalidEncoding;
534534
535 var salt: [default_salt_len]u8 = undefined;535 var salt: [default_salt_len]u8 = undefined;
536 crypto.random.bytes(&salt);536 io.random(&salt);
537537
538 var hash: [default_hash_len]u8 = undefined;538 var hash: [default_hash_len]u8 = undefined;
539 try kdf(allocator, &hash, password, &salt, params, mode, io);539 try kdf(allocator, &hash, password, &salt, params, mode, io);
lib/std/crypto/bcrypt.zig+72-39
...@@ -17,7 +17,7 @@ const HasherError = pwhash.HasherError;...@@ -17,7 +17,7 @@ const HasherError = pwhash.HasherError;
17const EncodingError = phc_format.Error;17const EncodingError = phc_format.Error;
18const Error = pwhash.Error;18const Error = pwhash.Error;
1919
20const salt_length: usize = 16;20pub const salt_length: usize = 16;
21const salt_str_length: usize = 22;21const salt_str_length: usize = 22;
22const ct_str_length: usize = 31;22const ct_str_length: usize = 31;
23const ct_length: usize = 24;23const ct_length: usize = 24;
...@@ -426,7 +426,7 @@ pub const Params = struct {...@@ -426,7 +426,7 @@ pub const Params = struct {
426426
427fn bcryptWithTruncation(427fn bcryptWithTruncation(
428 password: []const u8,428 password: []const u8,
429 salt: [salt_length]u8,429 salt: *const [salt_length]u8,
430 params: Params,430 params: Params,
431) [dk_length]u8 {431) [dk_length]u8 {
432 var state = State{};432 var state = State{};
...@@ -435,13 +435,13 @@ fn bcryptWithTruncation(...@@ -435,13 +435,13 @@ fn bcryptWithTruncation(
435 @memcpy(password_buf[0..trimmed_len], password[0..trimmed_len]);435 @memcpy(password_buf[0..trimmed_len], password[0..trimmed_len]);
436 password_buf[trimmed_len] = 0;436 password_buf[trimmed_len] = 0;
437 const passwordZ = password_buf[0 .. trimmed_len + 1];437 const passwordZ = password_buf[0 .. trimmed_len + 1];
438 state.expand(salt[0..], passwordZ);438 state.expand(salt, passwordZ);
439439
440 const rounds: u64 = @as(u64, 1) << params.rounds_log;440 const rounds: u64 = @as(u64, 1) << params.rounds_log;
441 var k: u64 = 0;441 var k: u64 = 0;
442 while (k < rounds) : (k += 1) {442 while (k < rounds) : (k += 1) {
443 state.expand0(passwordZ);443 state.expand0(passwordZ);
444 state.expand0(salt[0..]);444 state.expand0(salt);
445 }445 }
446 crypto.secureZero(u8, &password_buf);446 crypto.secureZero(u8, &password_buf);
447447
...@@ -467,7 +467,7 @@ fn bcryptWithTruncation(...@@ -467,7 +467,7 @@ fn bcryptWithTruncation(
467/// For key derivation, use `bcrypt.pbkdf()` or `bcrypt.opensshKdf()` instead.467/// For key derivation, use `bcrypt.pbkdf()` or `bcrypt.opensshKdf()` instead.
468pub fn bcrypt(468pub fn bcrypt(
469 password: []const u8,469 password: []const u8,
470 salt: [salt_length]u8,470 salt: *const [salt_length]u8,
471 params: Params,471 params: Params,
472) [dk_length]u8 {472) [dk_length]u8 {
473 if (password.len <= 72 or params.silently_truncate_password) {473 if (password.len <= 72 or params.silently_truncate_password) {
...@@ -475,7 +475,7 @@ pub fn bcrypt(...@@ -475,7 +475,7 @@ pub fn bcrypt(
475 }475 }
476476
477 var pre_hash: [HmacSha512.mac_length]u8 = undefined;477 var pre_hash: [HmacSha512.mac_length]u8 = undefined;
478 HmacSha512.create(&pre_hash, password, &salt);478 HmacSha512.create(&pre_hash, password, salt);
479479
480 const Encoder = crypt_format.Codec.Encoder;480 const Encoder = crypt_format.Codec.Encoder;
481 var pre_hash_b64: [Encoder.calcSize(pre_hash.len)]u8 = undefined;481 var pre_hash_b64: [Encoder.calcSize(pre_hash.len)]u8 = undefined;
...@@ -623,16 +623,16 @@ const crypt_format = struct {...@@ -623,16 +623,16 @@ const crypt_format = struct {
623623
624 fn strHashInternal(624 fn strHashInternal(
625 password: []const u8,625 password: []const u8,
626 salt: [salt_length]u8,626 salt: *const [salt_length]u8,
627 params: Params,627 params: Params,
628 ) [hash_length]u8 {628 ) [hash_length]u8 {
629 var dk = bcrypt(password, salt, params);629 var dk = bcrypt(password, salt, params);
630630
631 var salt_str: [salt_str_length]u8 = undefined;631 var salt_str: [salt_str_length]u8 = undefined;
632 _ = Codec.Encoder.encode(salt_str[0..], salt[0..]);632 _ = Codec.Encoder.encode(&salt_str, salt);
633633
634 var ct_str: [ct_str_length]u8 = undefined;634 var ct_str: [ct_str_length]u8 = undefined;
635 _ = Codec.Encoder.encode(ct_str[0..], dk[0..]);635 _ = Codec.Encoder.encode(&ct_str, dk[0..]);
636636
637 var s_buf: [hash_length]u8 = undefined;637 var s_buf: [hash_length]u8 = undefined;
638 const s = fmt.bufPrint(638 const s = fmt.bufPrint(
...@@ -657,21 +657,20 @@ const PhcFormatHasher = struct {...@@ -657,21 +657,20 @@ const PhcFormatHasher = struct {
657 hash: BinValue(dk_length),657 hash: BinValue(dk_length),
658 };658 };
659659
660 /// Return a non-deterministic hash of the password encoded as a PHC-format string660 /// Return a non-deterministic hash of the password encoded as a PHC-format string.
661 fn create(661 fn create(
662 password: []const u8,662 password: []const u8,
663 params: Params,663 params: Params,
664 buf: []u8,664 buf: []u8,
665 /// Filled with cryptographically secure entropy.
666 salt: *const [salt_length]u8,
665 ) HasherError![]const u8 {667 ) HasherError![]const u8 {
666 var salt: [salt_length]u8 = undefined;
667 crypto.random.bytes(&salt);
668
669 const hash = bcrypt(password, salt, params);668 const hash = bcrypt(password, salt, params);
670669
671 return phc_format.serialize(HashResult{670 return phc_format.serialize(HashResult{
672 .alg_id = alg_id,671 .alg_id = alg_id,
673 .r = params.rounds_log,672 .r = params.rounds_log,
674 .salt = try BinValue(salt_length).fromSlice(&salt),673 .salt = try BinValue(salt_length).fromSlice(salt),
675 .hash = try BinValue(dk_length).fromSlice(&hash),674 .hash = try BinValue(dk_length).fromSlice(&hash),
676 }, buf);675 }, buf);
677 }676 }
...@@ -688,11 +687,11 @@ const PhcFormatHasher = struct {...@@ -688,11 +687,11 @@ const PhcFormatHasher = struct {
688 if (hash_result.salt.len != salt_length or hash_result.hash.len != dk_length)687 if (hash_result.salt.len != salt_length or hash_result.hash.len != dk_length)
689 return HasherError.InvalidEncoding;688 return HasherError.InvalidEncoding;
690689
691 const params = Params{690 const params: Params = .{
692 .rounds_log = hash_result.r,691 .rounds_log = hash_result.r,
693 .silently_truncate_password = silently_truncate_password,692 .silently_truncate_password = silently_truncate_password,
694 };693 };
695 const hash = bcrypt(password, hash_result.salt.buf, params);694 const hash = bcrypt(password, &hash_result.salt.buf, params);
696 const expected_hash = hash_result.hash.constSlice();695 const expected_hash = hash_result.hash.constSlice();
697696
698 if (!mem.eql(u8, &hash, expected_hash)) return HasherError.PasswordVerificationFailed;697 if (!mem.eql(u8, &hash, expected_hash)) return HasherError.PasswordVerificationFailed;
...@@ -709,12 +708,11 @@ const CryptFormatHasher = struct {...@@ -709,12 +708,11 @@ const CryptFormatHasher = struct {
709 password: []const u8,708 password: []const u8,
710 params: Params,709 params: Params,
711 buf: []u8,710 buf: []u8,
711 /// Filled with cryptographically secure entropy.
712 salt: *const [salt_length]u8,
712 ) HasherError![]const u8 {713 ) HasherError![]const u8 {
713 if (buf.len < pwhash_str_length) return HasherError.NoSpaceLeft;714 if (buf.len < pwhash_str_length) return HasherError.NoSpaceLeft;
714715
715 var salt: [salt_length]u8 = undefined;
716 crypto.random.bytes(&salt);
717
718 const hash = crypt_format.strHashInternal(password, salt, params);716 const hash = crypt_format.strHashInternal(password, salt, params);
719 @memcpy(buf[0..hash.len], &hash);717 @memcpy(buf[0..hash.len], &hash);
720718
...@@ -736,9 +734,9 @@ const CryptFormatHasher = struct {...@@ -736,9 +734,9 @@ const CryptFormatHasher = struct {
736734
737 const salt_str = str[7..][0..salt_str_length];735 const salt_str = str[7..][0..salt_str_length];
738 var salt: [salt_length]u8 = undefined;736 var salt: [salt_length]u8 = undefined;
739 crypt_format.Codec.Decoder.decode(salt[0..], salt_str[0..]) catch return HasherError.InvalidEncoding;737 crypt_format.Codec.Decoder.decode(&salt, salt_str) catch return HasherError.InvalidEncoding;
740738
741 const wanted_s = crypt_format.strHashInternal(password, salt, .{739 const wanted_s = crypt_format.strHashInternal(password, &salt, .{
742 .rounds_log = rounds_log,740 .rounds_log = rounds_log,
743 .silently_truncate_password = silently_truncate_password,741 .silently_truncate_password = silently_truncate_password,
744 });742 });
...@@ -756,21 +754,28 @@ pub const HashOptions = struct {...@@ -756,21 +754,28 @@ pub const HashOptions = struct {
756 encoding: pwhash.Encoding,754 encoding: pwhash.Encoding,
757};755};
758756
759/// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key stretching function.757/// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key
760/// bcrypt is a computationally expensive and cache-hard function, explicitly designed to slow down exhaustive searches.758/// stretching function.
759///
760/// bcrypt is a computationally expensive and cache-hard function, explicitly
761/// designed to slow down exhaustive searches.
761///762///
762/// The function returns a string that includes all the parameters required for verification.763/// The function returns a string that includes all the parameters required for
764/// verification.
763///765///
764/// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes.766/// By design, bcrypt silently truncates passwords to 72 bytes. If this is an
765/// If this is an issue for your application, set the `silently_truncate_password` option to `false`.767/// issue for your application, set the `silently_truncate_password` option to
768/// `false`.
766pub fn strHash(769pub fn strHash(
767 password: []const u8,770 password: []const u8,
768 options: HashOptions,771 options: HashOptions,
769 out: []u8,772 out: []u8,
773 /// Filled with cryptographically secure entropy.
774 salt: *const [salt_length]u8,
770) Error![]const u8 {775) Error![]const u8 {
771 switch (options.encoding) {776 switch (options.encoding) {
772 .phc => return PhcFormatHasher.create(password, options.params, out),777 .phc => return PhcFormatHasher.create(password, options.params, out, salt),
773 .crypt => return CryptFormatHasher.create(password, options.params, out),778 .crypt => return CryptFormatHasher.create(password, options.params, out, salt),
774 }779 }
775}780}
776781
...@@ -796,8 +801,9 @@ pub fn strVerify(...@@ -796,8 +801,9 @@ pub fn strVerify(
796}801}
797802
798test "bcrypt codec" {803test "bcrypt codec" {
804 const io = testing.io;
799 var salt: [salt_length]u8 = undefined;805 var salt: [salt_length]u8 = undefined;
800 crypto.random.bytes(&salt);806 io.random(&salt);
801 var salt_str: [salt_str_length]u8 = undefined;807 var salt_str: [salt_str_length]u8 = undefined;
802 _ = crypt_format.Codec.Encoder.encode(salt_str[0..], salt[0..]);808 _ = crypt_format.Codec.Encoder.encode(salt_str[0..], salt[0..]);
803 var salt2: [salt_length]u8 = undefined;809 var salt2: [salt_length]u8 = undefined;
...@@ -806,14 +812,20 @@ test "bcrypt codec" {...@@ -806,14 +812,20 @@ test "bcrypt codec" {
806}812}
807813
808test "bcrypt crypt format" {814test "bcrypt crypt format" {
809 var hash_options = HashOptions{815 const io = testing.io;
816
817 var hash_options: HashOptions = .{
810 .params = .{ .rounds_log = 5, .silently_truncate_password = false },818 .params = .{ .rounds_log = 5, .silently_truncate_password = false },
811 .encoding = .crypt,819 .encoding = .crypt,
812 };820 };
813 var verify_options = VerifyOptions{ .silently_truncate_password = false };821 var verify_options: VerifyOptions = .{ .silently_truncate_password = false };
814822
815 var buf: [hash_length]u8 = undefined;823 var buf: [hash_length]u8 = undefined;
816 const s = try strHash("password", hash_options, &buf);824 const s = s: {
825 var salt: [salt_length]u8 = undefined;
826 io.random(&salt);
827 break :s try strHash("password", hash_options, &buf, &salt);
828 };
817829
818 try testing.expect(mem.startsWith(u8, s, crypt_format.prefix));830 try testing.expect(mem.startsWith(u8, s, crypt_format.prefix));
819 try strVerify(s, "password", verify_options);831 try strVerify(s, "password", verify_options);
...@@ -823,7 +835,11 @@ test "bcrypt crypt format" {...@@ -823,7 +835,11 @@ test "bcrypt crypt format" {
823 );835 );
824836
825 var long_buf: [hash_length]u8 = undefined;837 var long_buf: [hash_length]u8 = undefined;
826 var long_s = try strHash("password" ** 100, hash_options, &long_buf);838 var long_s = s: {
839 var salt: [salt_length]u8 = undefined;
840 io.random(&salt);
841 break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
842 };
827843
828 try testing.expect(mem.startsWith(u8, long_s, crypt_format.prefix));844 try testing.expect(mem.startsWith(u8, long_s, crypt_format.prefix));
829 try strVerify(long_s, "password" ** 100, verify_options);845 try strVerify(long_s, "password" ** 100, verify_options);
...@@ -834,7 +850,11 @@ test "bcrypt crypt format" {...@@ -834,7 +850,11 @@ test "bcrypt crypt format" {
834850
835 hash_options.params.silently_truncate_password = true;851 hash_options.params.silently_truncate_password = true;
836 verify_options.silently_truncate_password = true;852 verify_options.silently_truncate_password = true;
837 long_s = try strHash("password" ** 100, hash_options, &long_buf);853 long_s = s: {
854 var salt: [salt_length]u8 = undefined;
855 io.random(&salt);
856 break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
857 };
838 try strVerify(long_s, "password" ** 101, verify_options);858 try strVerify(long_s, "password" ** 101, verify_options);
839859
840 try strVerify(860 try strVerify(
...@@ -845,15 +865,20 @@ test "bcrypt crypt format" {...@@ -845,15 +865,20 @@ test "bcrypt crypt format" {
845}865}
846866
847test "bcrypt phc format" {867test "bcrypt phc format" {
848 var hash_options = HashOptions{868 const io = testing.io;
869 var hash_options: HashOptions = .{
849 .params = .{ .rounds_log = 5, .silently_truncate_password = false },870 .params = .{ .rounds_log = 5, .silently_truncate_password = false },
850 .encoding = .phc,871 .encoding = .phc,
851 };872 };
852 var verify_options = VerifyOptions{ .silently_truncate_password = false };873 var verify_options: VerifyOptions = .{ .silently_truncate_password = false };
853 const prefix = "$bcrypt$";874 const prefix = "$bcrypt$";
854875
855 var buf: [hash_length * 2]u8 = undefined;876 var buf: [hash_length * 2]u8 = undefined;
856 const s = try strHash("password", hash_options, &buf);877 const s = s: {
878 var salt: [salt_length]u8 = undefined;
879 io.random(&salt);
880 break :s try strHash("password", hash_options, &buf, &salt);
881 };
857882
858 try testing.expect(mem.startsWith(u8, s, prefix));883 try testing.expect(mem.startsWith(u8, s, prefix));
859 try strVerify(s, "password", verify_options);884 try strVerify(s, "password", verify_options);
...@@ -863,7 +888,11 @@ test "bcrypt phc format" {...@@ -863,7 +888,11 @@ test "bcrypt phc format" {
863 );888 );
864889
865 var long_buf: [hash_length * 2]u8 = undefined;890 var long_buf: [hash_length * 2]u8 = undefined;
866 var long_s = try strHash("password" ** 100, hash_options, &long_buf);891 var long_s = s: {
892 var salt: [salt_length]u8 = undefined;
893 io.random(&salt);
894 break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
895 };
867896
868 try testing.expect(mem.startsWith(u8, long_s, prefix));897 try testing.expect(mem.startsWith(u8, long_s, prefix));
869 try strVerify(long_s, "password" ** 100, verify_options);898 try strVerify(long_s, "password" ** 100, verify_options);
...@@ -874,7 +903,11 @@ test "bcrypt phc format" {...@@ -874,7 +903,11 @@ test "bcrypt phc format" {
874903
875 hash_options.params.silently_truncate_password = true;904 hash_options.params.silently_truncate_password = true;
876 verify_options.silently_truncate_password = true;905 verify_options.silently_truncate_password = true;
877 long_s = try strHash("password" ** 100, hash_options, &long_buf);906 long_s = s: {
907 var salt: [salt_length]u8 = undefined;
908 io.random(&salt);
909 break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
910 };
878 try strVerify(long_s, "password" ** 101, verify_options);911 try strVerify(long_s, "password" ** 101, verify_options);
879912
880 try strVerify(913 try strVerify(
lib/std/crypto/ecdsa.zig+17-11
...@@ -323,10 +323,10 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {...@@ -323,10 +323,10 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
323 }323 }
324324
325 /// Generate a new, random key pair.325 /// Generate a new, random key pair.
326 pub fn generate() KeyPair {326 pub fn generate(io: std.Io) KeyPair {
327 var random_seed: [seed_length]u8 = undefined;327 var random_seed: [seed_length]u8 = undefined;
328 while (true) {328 while (true) {
329 crypto.random.bytes(&random_seed);329 io.random(&random_seed);
330 return generateDeterministic(random_seed) catch {330 return generateDeterministic(random_seed) catch {
331 @branchHint(.unlikely);331 @branchHint(.unlikely);
332 continue;332 continue;
...@@ -417,12 +417,13 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {...@@ -417,12 +417,13 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
417test "Basic operations over EcdsaP384Sha384" {417test "Basic operations over EcdsaP384Sha384" {
418 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;418 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
419419
420 const io = testing.io;
420 const Scheme = EcdsaP384Sha384;421 const Scheme = EcdsaP384Sha384;
421 const kp = Scheme.KeyPair.generate();422 const kp = Scheme.KeyPair.generate(io);
422 const msg = "test";423 const msg = "test";
423424
424 var noise: [Scheme.noise_length]u8 = undefined;425 var noise: [Scheme.noise_length]u8 = undefined;
425 crypto.random.bytes(&noise);426 io.random(&noise);
426 const sig = try kp.sign(msg, noise);427 const sig = try kp.sign(msg, noise);
427 try sig.verify(msg, kp.public_key);428 try sig.verify(msg, kp.public_key);
428429
...@@ -433,12 +434,13 @@ test "Basic operations over EcdsaP384Sha384" {...@@ -433,12 +434,13 @@ test "Basic operations over EcdsaP384Sha384" {
433test "Basic operations over Secp256k1" {434test "Basic operations over Secp256k1" {
434 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;435 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
435436
437 const io = testing.io;
436 const Scheme = EcdsaSecp256k1Sha256oSha256;438 const Scheme = EcdsaSecp256k1Sha256oSha256;
437 const kp = Scheme.KeyPair.generate();439 const kp = Scheme.KeyPair.generate(io);
438 const msg = "test";440 const msg = "test";
439441
440 var noise: [Scheme.noise_length]u8 = undefined;442 var noise: [Scheme.noise_length]u8 = undefined;
441 crypto.random.bytes(&noise);443 io.random(&noise);
442 const sig = try kp.sign(msg, noise);444 const sig = try kp.sign(msg, noise);
443 try sig.verify(msg, kp.public_key);445 try sig.verify(msg, kp.public_key);
444446
...@@ -449,12 +451,13 @@ test "Basic operations over Secp256k1" {...@@ -449,12 +451,13 @@ test "Basic operations over Secp256k1" {
449test "Basic operations over EcdsaP384Sha256" {451test "Basic operations over EcdsaP384Sha256" {
450 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;452 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
451453
454 const io = testing.io;
452 const Scheme = Ecdsa(crypto.ecc.P384, crypto.hash.sha2.Sha256);455 const Scheme = Ecdsa(crypto.ecc.P384, crypto.hash.sha2.Sha256);
453 const kp = Scheme.KeyPair.generate();456 const kp = Scheme.KeyPair.generate(io);
454 const msg = "test";457 const msg = "test";
455458
456 var noise: [Scheme.noise_length]u8 = undefined;459 var noise: [Scheme.noise_length]u8 = undefined;
457 crypto.random.bytes(&noise);460 io.random(&noise);
458 const sig = try kp.sign(msg, noise);461 const sig = try kp.sign(msg, noise);
459 try sig.verify(msg, kp.public_key);462 try sig.verify(msg, kp.public_key);
460463
...@@ -502,8 +505,10 @@ test "Verifying a existing signature with EcdsaP384Sha256" {...@@ -502,8 +505,10 @@ test "Verifying a existing signature with EcdsaP384Sha256" {
502test "Prehashed message operations" {505test "Prehashed message operations" {
503 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;506 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
504507
508 const io = testing.io;
509
505 const Scheme = EcdsaP256Sha256;510 const Scheme = EcdsaP256Sha256;
506 const kp = Scheme.KeyPair.generate();511 const kp = Scheme.KeyPair.generate(io);
507 const msg = "test message for prehashed signing";512 const msg = "test message for prehashed signing";
508513
509 const Hash = crypto.hash.sha2.Sha256;514 const Hash = crypto.hash.sha2.Sha256;
...@@ -518,7 +523,7 @@ test "Prehashed message operations" {...@@ -518,7 +523,7 @@ test "Prehashed message operations" {
518 try testing.expectError(error.SignatureVerificationFailed, sig.verifyPrehashed(bad_hash, kp.public_key));523 try testing.expectError(error.SignatureVerificationFailed, sig.verifyPrehashed(bad_hash, kp.public_key));
519524
520 var noise: [Scheme.noise_length]u8 = undefined;525 var noise: [Scheme.noise_length]u8 = undefined;
521 crypto.random.bytes(&noise);526 io.random(&noise);
522 const sig_with_noise = try kp.signPrehashed(msg_hash, noise);527 const sig_with_noise = try kp.signPrehashed(msg_hash, noise);
523 try sig_with_noise.verifyPrehashed(msg_hash, kp.public_key);528 try sig_with_noise.verifyPrehashed(msg_hash, kp.public_key);
524529
...@@ -1628,8 +1633,9 @@ fn tvTry(comptime Scheme: type, vector: TestVector) !void {...@@ -1628,8 +1633,9 @@ fn tvTry(comptime Scheme: type, vector: TestVector) !void {
1628test "Sec1 encoding/decoding" {1633test "Sec1 encoding/decoding" {
1629 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;1634 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
16301635
1636 const io = testing.io;
1631 const Scheme = EcdsaP384Sha384;1637 const Scheme = EcdsaP384Sha384;
1632 const kp = Scheme.KeyPair.generate();1638 const kp = Scheme.KeyPair.generate(io);
1633 const pk = kp.public_key;1639 const pk = kp.public_key;
1634 const pk_compressed_sec1 = pk.toCompressedSec1();1640 const pk_compressed_sec1 = pk.toCompressedSec1();
1635 const pk_recovered1 = try Scheme.PublicKey.fromSec1(&pk_compressed_sec1);1641 const pk_recovered1 = try Scheme.PublicKey.fromSec1(&pk_compressed_sec1);
lib/std/crypto/hybrid_kem.zig+74-61
...@@ -174,43 +174,56 @@ pub fn HybridKem(comptime params: Params) type {...@@ -174,43 +174,56 @@ pub fn HybridKem(comptime params: Params) type {
174 return .{ .bytes = buf.* };174 return .{ .bytes = buf.* };
175 }175 }
176176
177 /// Generates a shared secret and encapsulates it for the public key.177 /// Generates a shared secret, encapsulated for the public key,
178 /// If `seed` is `null`, uses random bytes from `std.crypto.random`.178 /// using random bytes.
179 /// If `seed` is set, encapsulation is deterministic (for testing only).179 ///
180 pub fn encaps(self: PublicKey, seed: ?[]const u8) !EncapsulatedSecret {180 /// This is recommended over `encapsDeterministic`.
181 const pq_nek = params.PqKem.PublicKey.encoded_length;181 pub fn encaps(pk: PublicKey, io: std.Io) !EncapsulatedSecret {
182 const ek_pq = try params.PqKem.PublicKey.fromBytes(self.bytes[0..pq_nek]);
183 const ek_t = self.bytes[pq_nek..][0..params.Group.element_length];
184
185 var seed_pq: [32]u8 = undefined;182 var seed_pq: [32]u8 = undefined;
186 var seed_t_expanded: [params.Group.seed_length]u8 = undefined;183 io.random(&seed_pq);
184 var seed_t: [32]u8 = undefined;
185 io.random(&seed_t);
186 var seed_t_expanded: [params.Group.seed_length]u8 = try expandRandomnessSeed(seed_t);
187 return encapsInner(pk, &seed_pq, &seed_t_expanded);
188 }
187189
188 if (seed) |r| {190 /// Generates a shared secret, encapsulated for the public key,
189 if (r.len < 32) return error.InsufficientRandomness;191 /// using the provided seed.
190 seed_pq = r[0..32].*;192 ///
193 /// Calling `encaps` instead is recommended.
194 pub fn encapsDeterministic(pk: PublicKey, seed: []const u8) !EncapsulatedSecret {
195 if (seed.len < 32) return error.InsufficientRandomness;
196 var seed_pq: [32]u8 = seed[0..32].*;
197 var seed_t_expanded: [params.Group.seed_length]u8 = undefined;
191198
192 const t_randomness = r[32..];199 const t_randomness = seed[32..];
200 if (t_randomness.len < params.Group.seed_length) {
201 // Provided randomness is shorter than seed_length, use it directly
202 // (test vectors provide just enough for randomScalar)
203 @memcpy(seed_t_expanded[0..t_randomness.len], t_randomness);
204 // Pad the rest with zeros if needed (shouldn't be used by randomScalar)
193 if (t_randomness.len < params.Group.seed_length) {205 if (t_randomness.len < params.Group.seed_length) {
194 // Provided randomness is shorter than seed_length, use it directly206 @memset(seed_t_expanded[t_randomness.len..], 0);
195 // (test vectors provide just enough for randomScalar)
196 @memcpy(seed_t_expanded[0..t_randomness.len], t_randomness);
197 // Pad the rest with zeros if needed (shouldn't be used by randomScalar)
198 if (t_randomness.len < params.Group.seed_length) {
199 @memset(seed_t_expanded[t_randomness.len..], 0);
200 }
201 } else {
202 // Full randomness provided
203 @memcpy(&seed_t_expanded, t_randomness[0..params.Group.seed_length]);
204 }207 }
205 } else {208 } else {
206 crypto.random.bytes(&seed_pq);209 // Full randomness provided
207 var seed_t: [32]u8 = undefined;210 @memcpy(&seed_t_expanded, t_randomness[0..params.Group.seed_length]);
208 crypto.random.bytes(&seed_t);
209 seed_t_expanded = try expandRandomnessSeed(seed_t);
210 }211 }
211212
212 const pq_encap = ek_pq.encaps(seed_pq);213 return encapsInner(pk, &seed_pq, &seed_t_expanded);
213 const sk_e = try params.Group.randomScalar(&seed_t_expanded);214 }
215
216 fn encapsInner(
217 pk: PublicKey,
218 seed_pq: *[32]u8,
219 seed_t_expanded: *[params.Group.seed_length]u8,
220 ) !EncapsulatedSecret {
221 const pq_nek = params.PqKem.PublicKey.encoded_length;
222 const ek_pq = try params.PqKem.PublicKey.fromBytes(pk.bytes[0..pq_nek]);
223 const ek_t = pk.bytes[pq_nek..][0..params.Group.element_length];
224
225 const pq_encap = ek_pq.encapsDeterministic(seed_pq);
226 const sk_e = try params.Group.randomScalar(seed_t_expanded);
214 const ct_t_point = try params.Group.mulBase(sk_e);227 const ct_t_point = try params.Group.mulBase(sk_e);
215 const ct_t = if (is_nist_curve) params.Group.encodePoint(ct_t_point) else ct_t_point;228 const ct_t = if (is_nist_curve) params.Group.encodePoint(ct_t_point) else ct_t_point;
216229
...@@ -280,9 +293,9 @@ pub fn HybridKem(comptime params: Params) type {...@@ -280,9 +293,9 @@ pub fn HybridKem(comptime params: Params) type {
280 }293 }
281294
282 /// Generates a new random key pair.295 /// Generates a new random key pair.
283 pub fn generate() !KeyPair {296 pub fn generate(io: std.Io) !KeyPair {
284 var seed: [params.Nseed]u8 = undefined;297 var seed: [params.Nseed]u8 = undefined;
285 crypto.random.bytes(&seed);298 io.random(&seed);
286 return generateDeterministic(seed);299 return generateDeterministic(seed);
287 }300 }
288 };301 };
...@@ -386,7 +399,7 @@ test "MLKEM768-X25519 basic round trip" {...@@ -386,7 +399,7 @@ test "MLKEM768-X25519 basic round trip" {
386 var enc_seed: [64]u8 = undefined;399 var enc_seed: [64]u8 = undefined;
387 @memset(&enc_seed, 0x43);400 @memset(&enc_seed, 0x43);
388401
389 const encap_result = try kp.public_key.encaps(&enc_seed);402 const encap_result = try kp.public_key.encapsDeterministic(&enc_seed);
390 const ss_decap = try kp.secret_key.decaps(&encap_result.ciphertext);403 const ss_decap = try kp.secret_key.decaps(&encap_result.ciphertext);
391404
392 try testing.expectEqualSlices(u8, &encap_result.shared_secret, &ss_decap);405 try testing.expectEqualSlices(u8, &encap_result.shared_secret, &ss_decap);
...@@ -408,7 +421,7 @@ test "MLKEM768-X25519 test vector 0" {...@@ -408,7 +421,7 @@ test "MLKEM768-X25519 test vector 0" {
408 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);421 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
409 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());422 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
410423
411 const enc_result = try kp.public_key.encaps(&randomness);424 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
412 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);425 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
413 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);426 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
414427
...@@ -432,7 +445,7 @@ test "MLKEM768-X25519 test vector 1" {...@@ -432,7 +445,7 @@ test "MLKEM768-X25519 test vector 1" {
432 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);445 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
433 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());446 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
434447
435 const enc_result = try kp.public_key.encaps(&randomness);448 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
436 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);449 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
437 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);450 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
438451
...@@ -456,7 +469,7 @@ test "MLKEM768-X25519 test vector 2" {...@@ -456,7 +469,7 @@ test "MLKEM768-X25519 test vector 2" {
456 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);469 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
457 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());470 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
458471
459 const enc_result = try kp.public_key.encaps(&randomness);472 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
460 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);473 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
461 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);474 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
462475
...@@ -480,7 +493,7 @@ test "MLKEM768-X25519 test vector 3" {...@@ -480,7 +493,7 @@ test "MLKEM768-X25519 test vector 3" {
480 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);493 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
481 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());494 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
482495
483 const enc_result = try kp.public_key.encaps(&randomness);496 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
484 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);497 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
485 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);498 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
486499
...@@ -504,7 +517,7 @@ test "MLKEM768-X25519 test vector 4" {...@@ -504,7 +517,7 @@ test "MLKEM768-X25519 test vector 4" {
504 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);517 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
505 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());518 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
506519
507 const enc_result = try kp.public_key.encaps(&randomness);520 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
508 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);521 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
509 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);522 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
510523
...@@ -528,7 +541,7 @@ test "MLKEM768-X25519 test vector 5" {...@@ -528,7 +541,7 @@ test "MLKEM768-X25519 test vector 5" {
528 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);541 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
529 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());542 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
530543
531 const enc_result = try kp.public_key.encaps(&randomness);544 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
532 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);545 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
533 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);546 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
534547
...@@ -552,7 +565,7 @@ test "MLKEM768-X25519 test vector 6" {...@@ -552,7 +565,7 @@ test "MLKEM768-X25519 test vector 6" {
552 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);565 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
553 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());566 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
554567
555 const enc_result = try kp.public_key.encaps(&randomness);568 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
556 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);569 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
557 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);570 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
558571
...@@ -576,7 +589,7 @@ test "MLKEM768-X25519 test vector 7" {...@@ -576,7 +589,7 @@ test "MLKEM768-X25519 test vector 7" {
576 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);589 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
577 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());590 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
578591
579 const enc_result = try kp.public_key.encaps(&randomness);592 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
580 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);593 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
581 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);594 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
582595
...@@ -600,7 +613,7 @@ test "MLKEM768-X25519 test vector 8" {...@@ -600,7 +613,7 @@ test "MLKEM768-X25519 test vector 8" {
600 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);613 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
601 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());614 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
602615
603 const enc_result = try kp.public_key.encaps(&randomness);616 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
604 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);617 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
605 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);618 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
606619
...@@ -624,7 +637,7 @@ test "MLKEM768-X25519 test vector 9" {...@@ -624,7 +637,7 @@ test "MLKEM768-X25519 test vector 9" {
624 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);637 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
625 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());638 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
626639
627 const enc_result = try kp.public_key.encaps(&randomness);640 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
628 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);641 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
629 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);642 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
630643
...@@ -648,7 +661,7 @@ test "MLKEM768-P256 test vector 0" {...@@ -648,7 +661,7 @@ test "MLKEM768-P256 test vector 0" {
648 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);661 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
649 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());662 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
650663
651 const enc_result = try kp.public_key.encaps(&randomness);664 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
652 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);665 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
653 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);666 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
654667
...@@ -672,7 +685,7 @@ test "MLKEM768-P256 test vector 1" {...@@ -672,7 +685,7 @@ test "MLKEM768-P256 test vector 1" {
672 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);685 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
673 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());686 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
674687
675 const enc_result = try kp.public_key.encaps(&randomness);688 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
676 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);689 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
677 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);690 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
678691
...@@ -696,7 +709,7 @@ test "MLKEM768-P256 test vector 2" {...@@ -696,7 +709,7 @@ test "MLKEM768-P256 test vector 2" {
696 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);709 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
697 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());710 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
698711
699 const enc_result = try kp.public_key.encaps(&randomness);712 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
700 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);713 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
701 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);714 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
702715
...@@ -720,7 +733,7 @@ test "MLKEM768-P256 test vector 3" {...@@ -720,7 +733,7 @@ test "MLKEM768-P256 test vector 3" {
720 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);733 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
721 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());734 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
722735
723 const enc_result = try kp.public_key.encaps(&randomness);736 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
724 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);737 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
725 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);738 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
726739
...@@ -744,7 +757,7 @@ test "MLKEM768-P256 test vector 4" {...@@ -744,7 +757,7 @@ test "MLKEM768-P256 test vector 4" {
744 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);757 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
745 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());758 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
746759
747 const enc_result = try kp.public_key.encaps(&randomness);760 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
748 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);761 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
749 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);762 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
750763
...@@ -768,7 +781,7 @@ test "MLKEM768-P256 test vector 5" {...@@ -768,7 +781,7 @@ test "MLKEM768-P256 test vector 5" {
768 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);781 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
769 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());782 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
770783
771 const enc_result = try kp.public_key.encaps(&randomness);784 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
772 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);785 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
773 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);786 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
774787
...@@ -792,7 +805,7 @@ test "MLKEM768-P256 test vector 6" {...@@ -792,7 +805,7 @@ test "MLKEM768-P256 test vector 6" {
792 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);805 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
793 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());806 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
794807
795 const enc_result = try kp.public_key.encaps(&randomness);808 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
796 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);809 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
797 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);810 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
798811
...@@ -816,7 +829,7 @@ test "MLKEM768-P256 test vector 7" {...@@ -816,7 +829,7 @@ test "MLKEM768-P256 test vector 7" {
816 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);829 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
817 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());830 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
818831
819 const enc_result = try kp.public_key.encaps(&randomness);832 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
820 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);833 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
821 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);834 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
822835
...@@ -840,7 +853,7 @@ test "MLKEM768-P256 test vector 8" {...@@ -840,7 +853,7 @@ test "MLKEM768-P256 test vector 8" {
840 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);853 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
841 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());854 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
842855
843 const enc_result = try kp.public_key.encaps(&randomness);856 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
844 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);857 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
845 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);858 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
846859
...@@ -864,7 +877,7 @@ test "MLKEM768-P256 test vector 9" {...@@ -864,7 +877,7 @@ test "MLKEM768-P256 test vector 9" {
864 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);877 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
865 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());878 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
866879
867 const enc_result = try kp.public_key.encaps(&randomness);880 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
868 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);881 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
869 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);882 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
870883
...@@ -888,7 +901,7 @@ test "MLKEM1024-P384 test vector 0" {...@@ -888,7 +901,7 @@ test "MLKEM1024-P384 test vector 0" {
888 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);901 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
889 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());902 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
890903
891 const enc_result = try kp.public_key.encaps(&randomness);904 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
892 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);905 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
893 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);906 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
894907
...@@ -912,7 +925,7 @@ test "MLKEM1024-P384 test vector 1" {...@@ -912,7 +925,7 @@ test "MLKEM1024-P384 test vector 1" {
912 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);925 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
913 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());926 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
914927
915 const enc_result = try kp.public_key.encaps(&randomness);928 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
916 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);929 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
917 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);930 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
918931
...@@ -936,7 +949,7 @@ test "MLKEM1024-P384 test vector 2" {...@@ -936,7 +949,7 @@ test "MLKEM1024-P384 test vector 2" {
936 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);949 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
937 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());950 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
938951
939 const enc_result = try kp.public_key.encaps(&randomness);952 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
940 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);953 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
941 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);954 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
942955
...@@ -960,7 +973,7 @@ test "MLKEM1024-P384 test vector 3" {...@@ -960,7 +973,7 @@ test "MLKEM1024-P384 test vector 3" {
960 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);973 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
961 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());974 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
962975
963 const enc_result = try kp.public_key.encaps(&randomness);976 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
964 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);977 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
965 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);978 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
966979
...@@ -984,7 +997,7 @@ test "MLKEM1024-P384 test vector 4" {...@@ -984,7 +997,7 @@ test "MLKEM1024-P384 test vector 4" {
984 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);997 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
985 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());998 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
986999
987 const enc_result = try kp.public_key.encaps(&randomness);1000 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
988 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);1001 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
989 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);1002 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
9901003
...@@ -1008,7 +1021,7 @@ test "MLKEM1024-P384 test vector 5" {...@@ -1008,7 +1021,7 @@ test "MLKEM1024-P384 test vector 5" {
1008 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);1021 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
1009 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());1022 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
10101023
1011 const enc_result = try kp.public_key.encaps(&randomness);1024 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
1012 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);1025 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
1013 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);1026 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
10141027
...@@ -1032,7 +1045,7 @@ test "MLKEM1024-P384 test vector 6" {...@@ -1032,7 +1045,7 @@ test "MLKEM1024-P384 test vector 6" {
1032 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);1045 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
1033 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());1046 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
10341047
1035 const enc_result = try kp.public_key.encaps(&randomness);1048 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
1036 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);1049 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
1037 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);1050 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
10381051
...@@ -1056,7 +1069,7 @@ test "MLKEM1024-P384 test vector 7" {...@@ -1056,7 +1069,7 @@ test "MLKEM1024-P384 test vector 7" {
1056 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);1069 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
1057 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());1070 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
10581071
1059 const enc_result = try kp.public_key.encaps(&randomness);1072 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
1060 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);1073 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
1061 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);1074 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
10621075
...@@ -1080,7 +1093,7 @@ test "MLKEM1024-P384 test vector 8" {...@@ -1080,7 +1093,7 @@ test "MLKEM1024-P384 test vector 8" {
1080 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);1093 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
1081 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());1094 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
10821095
1083 const enc_result = try kp.public_key.encaps(&randomness);1096 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
1084 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);1097 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
1085 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);1098 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
10861099
lib/std/crypto/ml_dsa.zig+10-9
...@@ -2019,12 +2019,9 @@ fn MLDSAImpl(comptime p: Params) type {...@@ -2019,12 +2019,9 @@ fn MLDSAImpl(comptime p: Params) type {
2019 secret_key: SecretKey,2019 secret_key: SecretKey,
20202020
2021 /// Generate a new random key pair.2021 /// Generate a new random key pair.
2022 /// This uses the system's cryptographically secure random number generator.2022 pub fn generate(io: std.Io) KeyPair {
2023 ///
2024 /// `crypto.random.bytes` must be supported by the target.
2025 pub fn generate() KeyPair {
2026 var seed: [Self.seed_length]u8 = undefined;2023 var seed: [Self.seed_length]u8 = undefined;
2027 crypto.random.bytes(&seed);2024 io.random(&seed);
2028 return generateDeterministic(seed) catch unreachable;2025 return generateDeterministic(seed) catch unreachable;
2029 }2026 }
20302027
...@@ -3198,8 +3195,9 @@ test "ML-DSA-87 KAT test vector 0" {...@@ -3198,8 +3195,9 @@ test "ML-DSA-87 KAT test vector 0" {
3198}3195}
31993196
3200test "KeyPair API - generate and sign" {3197test "KeyPair API - generate and sign" {
3198 const io = std.testing.io;
3201 // Test the new KeyPair API with random generation3199 // Test the new KeyPair API with random generation
3202 const kp = MLDSA44.KeyPair.generate();3200 const kp = MLDSA44.KeyPair.generate(io);
3203 const msg = "Test message for KeyPair API";3201 const msg = "Test message for KeyPair API";
32043202
3205 // Sign with deterministic mode (no noise)3203 // Sign with deterministic mode (no noise)
...@@ -3222,8 +3220,9 @@ test "KeyPair API - generateDeterministic" {...@@ -3222,8 +3220,9 @@ test "KeyPair API - generateDeterministic" {
3222}3220}
32233221
3224test "KeyPair API - fromSecretKey" {3222test "KeyPair API - fromSecretKey" {
3223 const io = std.testing.io;
3225 // Generate a key pair3224 // Generate a key pair
3226 const kp1 = MLDSA44.KeyPair.generate();3225 const kp1 = MLDSA44.KeyPair.generate(io);
32273226
3228 // Derive public key from secret key3227 // Derive public key from secret key
3229 const kp2 = try MLDSA44.KeyPair.fromSecretKey(kp1.secret_key);3228 const kp2 = try MLDSA44.KeyPair.fromSecretKey(kp1.secret_key);
...@@ -3235,8 +3234,9 @@ test "KeyPair API - fromSecretKey" {...@@ -3235,8 +3234,9 @@ test "KeyPair API - fromSecretKey" {
3235}3234}
32363235
3237test "Signature verification with noise" {3236test "Signature verification with noise" {
3237 const io = std.testing.io;
3238 // Test signing with randomness (hedged signatures)3238 // Test signing with randomness (hedged signatures)
3239 const kp = MLDSA65.KeyPair.generate();3239 const kp = MLDSA65.KeyPair.generate(io);
3240 const msg = "Message to be signed with randomness";3240 const msg = "Message to be signed with randomness";
32413241
3242 // Create some noise3242 // Create some noise
...@@ -3250,8 +3250,9 @@ test "Signature verification with noise" {...@@ -3250,8 +3250,9 @@ test "Signature verification with noise" {
3250}3250}
32513251
3252test "Signature verification failure" {3252test "Signature verification failure" {
3253 const io = std.testing.io;
3253 // Test that invalid signatures are rejected3254 // Test that invalid signatures are rejected
3254 const kp = MLDSA44.KeyPair.generate();3255 const kp = MLDSA44.KeyPair.generate(io);
3255 const msg = "Original message";3256 const msg = "Original message";
3256 const sig = try kp.sign(msg, null);3257 const sig = try kp.sign(msg, null);
32573258
lib/std/crypto/ml_kem.zig+29-20
...@@ -244,32 +244,41 @@ fn Kyber(comptime p: Params) type {...@@ -244,32 +244,41 @@ fn Kyber(comptime p: Params) type {
244 /// Size of a serialized representation of the key, in bytes.244 /// Size of a serialized representation of the key, in bytes.
245 pub const encoded_length = InnerPk.encoded_length;245 pub const encoded_length = InnerPk.encoded_length;
246246
247 /// Generates a shared secret, and encapsulates it for the public key.247 /// Generates a shared secret, encapsulated for the public key,
248 /// If `seed` is `null`, a random seed is used. This is recommended.248 /// using random bytes.
249 /// If `seed` is set, encapsulation is deterministic.249 ///
250 pub fn encaps(pk: PublicKey, seed_: ?[encaps_seed_length]u8) EncapsulatedSecret {250 /// This is recommended over `encapsDeterministic`.
251 pub fn encaps(pk: PublicKey, io: std.Io) EncapsulatedSecret {
251 var m: [inner_plaintext_length]u8 = undefined;252 var m: [inner_plaintext_length]u8 = undefined;
253 io.random(&m);
254 return encapsInner(pk, &m);
255 }
252256
253 if (seed_) |seed| {257 /// Generates a shared secret, encapsulated for the public key,
254 if (p.ml_kem) {258 /// using the provided seed.
255 @memcpy(&m, &seed);259 ///
256 } else {260 /// Calling `encaps` instead is recommended.
257 // m = H(seed)261 pub fn encapsDeterministic(pk: PublicKey, seed: *const [encaps_seed_length]u8) EncapsulatedSecret {
258 sha3.Sha3_256.hash(&seed, &m, .{});262 var m: [inner_plaintext_length]u8 = undefined;
259 }263 if (p.ml_kem) {
264 @memcpy(&m, seed);
260 } else {265 } else {
261 crypto.random.bytes(&m);266 // m = H(seed)
267 sha3.Sha3_256.hash(seed, &m, .{});
262 }268 }
269 return encapsInner(pk, &m);
270 }
263271
272 fn encapsInner(pk: PublicKey, m: *[inner_plaintext_length]u8) EncapsulatedSecret {
264 // (K', r) = G(m ‖ H(pk))273 // (K', r) = G(m ‖ H(pk))
265 var kr: [inner_plaintext_length + h_length]u8 = undefined;274 var kr: [inner_plaintext_length + h_length]u8 = undefined;
266 var g = sha3.Sha3_512.init(.{});275 var g = sha3.Sha3_512.init(.{});
267 g.update(&m);276 g.update(m);
268 g.update(&pk.hpk);277 g.update(&pk.hpk);
269 g.final(&kr);278 g.final(&kr);
270279
271 // c = innerEncrypt(pk, m, r)280 // c = innerEncrypt(pk, m, r)
272 const ct = pk.pk.encrypt(&m, kr[32..64]);281 const ct = pk.pk.encrypt(m, kr[32..64]);
273282
274 if (p.ml_kem) {283 if (p.ml_kem) {
275 return EncapsulatedSecret{284 return EncapsulatedSecret{
...@@ -398,10 +407,10 @@ fn Kyber(comptime p: Params) type {...@@ -398,10 +407,10 @@ fn Kyber(comptime p: Params) type {
398 }407 }
399408
400 /// Generate a new, random key pair.409 /// Generate a new, random key pair.
401 pub fn generate() KeyPair {410 pub fn generate(io: std.Io) KeyPair {
402 var random_seed: [seed_length]u8 = undefined;411 var random_seed: [seed_length]u8 = undefined;
403 while (true) {412 while (true) {
404 crypto.random.bytes(&random_seed);413 io.random(&random_seed);
405 return generateDeterministic(random_seed) catch {414 return generateDeterministic(random_seed) catch {
406 @branchHint(.unlikely);415 @branchHint(.unlikely);
407 continue;416 continue;
...@@ -1634,15 +1643,15 @@ test "Test happy flow" {...@@ -1634,15 +1643,15 @@ test "Test happy flow" {
1634 }1643 }
1635 inline for (modes) |mode| {1644 inline for (modes) |mode| {
1636 for (0..10) |i| {1645 for (0..10) |i| {
1637 seed[0] = @as(u8, @intCast(i));1646 seed[0] = @intCast(i);
1638 const kp = try mode.KeyPair.generateDeterministic(seed);1647 const kp = try mode.KeyPair.generateDeterministic(seed);
1639 const sk = try mode.SecretKey.fromBytes(&kp.secret_key.toBytes());1648 const sk = try mode.SecretKey.fromBytes(&kp.secret_key.toBytes());
1640 try testing.expectEqual(sk, kp.secret_key);1649 try testing.expectEqual(sk, kp.secret_key);
1641 const pk = try mode.PublicKey.fromBytes(&kp.public_key.toBytes());1650 const pk = try mode.PublicKey.fromBytes(&kp.public_key.toBytes());
1642 try testing.expectEqual(pk, kp.public_key);1651 try testing.expectEqual(pk, kp.public_key);
1643 for (0..10) |j| {1652 for (0..10) |j| {
1644 seed[1] = @as(u8, @intCast(j));1653 seed[1] = @intCast(j);
1645 const e = pk.encaps(seed[0..32].*);1654 const e = pk.encapsDeterministic(seed[0..32]);
1646 try testing.expectEqual(e.shared_secret, try sk.decaps(&e.ciphertext));1655 try testing.expectEqual(e.shared_secret, try sk.decaps(&e.ciphertext));
1647 }1656 }
1648 }1657 }
...@@ -1695,7 +1704,7 @@ fn testNistKat(mode: type, hash: []const u8) !void {...@@ -1695,7 +1704,7 @@ fn testNistKat(mode: type, hash: []const u8) !void {
1695 g2.fill(kseed[32..64]);1704 g2.fill(kseed[32..64]);
1696 g2.fill(&eseed);1705 g2.fill(&eseed);
1697 const kp = try mode.KeyPair.generateDeterministic(kseed);1706 const kp = try mode.KeyPair.generateDeterministic(kseed);
1698 const e = kp.public_key.encaps(eseed);1707 const e = kp.public_key.encapsDeterministic(&eseed);
1699 const ss2 = try kp.secret_key.decaps(&e.ciphertext);1708 const ss2 = try kp.secret_key.decaps(&e.ciphertext);
1700 try testing.expectEqual(ss2, e.shared_secret);1709 try testing.expectEqual(ss2, e.shared_secret);
1701 try fw.writer.print("pk = {X}\n", .{&kp.public_key.toBytes()});1710 try fw.writer.print("pk = {X}\n", .{&kp.public_key.toBytes()});
lib/std/crypto/pcurves/p256.zig+2-2
...@@ -122,8 +122,8 @@ pub const P256 = struct {...@@ -122,8 +122,8 @@ pub const P256 = struct {
122 }122 }
123123
124 /// Return a random point.124 /// Return a random point.
125 pub fn random() P256 {125 pub fn random(io: std.Io) P256 {
126 const n = scalar.random(.little);126 const n = scalar.random(io, .little);
127 return basePoint.mul(n, .little) catch unreachable;127 return basePoint.mul(n, .little) catch unreachable;
128 }128 }
129129
lib/std/crypto/pcurves/p256/scalar.zig+4-4
...@@ -68,8 +68,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)...@@ -68,8 +68,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)
68}68}
6969
70/// Return a random scalar70/// Return a random scalar
71pub fn random(endian: std.builtin.Endian) CompressedScalar {71pub fn random(io: std.Io, endian: std.builtin.Endian) CompressedScalar {
72 return Scalar.random().toBytes(endian);72 return Scalar.random(io).toBytes(endian);
73}73}
7474
75/// A scalar in unpacked representation.75/// A scalar in unpacked representation.
...@@ -170,10 +170,10 @@ pub const Scalar = struct {...@@ -170,10 +170,10 @@ pub const Scalar = struct {
170 }170 }
171171
172 /// Return a random scalar < L.172 /// Return a random scalar < L.
173 pub fn random() Scalar {173 pub fn random(io: std.Io) Scalar {
174 var s: [48]u8 = undefined;174 var s: [48]u8 = undefined;
175 while (true) {175 while (true) {
176 crypto.random.bytes(&s);176 io.random(&s);
177 const n = Scalar.fromBytes48(s, .little);177 const n = Scalar.fromBytes48(s, .little);
178 if (!n.isZero()) {178 if (!n.isZero()) {
179 return n;179 return n;
lib/std/crypto/pcurves/p384.zig+2-2
...@@ -122,8 +122,8 @@ pub const P384 = struct {...@@ -122,8 +122,8 @@ pub const P384 = struct {
122 }122 }
123123
124 /// Return a random point.124 /// Return a random point.
125 pub fn random() P384 {125 pub fn random(io: std.Io) P384 {
126 const n = scalar.random(.little);126 const n = scalar.random(io, .little);
127 return basePoint.mul(n, .little) catch unreachable;127 return basePoint.mul(n, .little) catch unreachable;
128 }128 }
129129
lib/std/crypto/pcurves/p384/scalar.zig+4-4
...@@ -63,8 +63,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)...@@ -63,8 +63,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)
63}63}
6464
65/// Return a random scalar65/// Return a random scalar
66pub fn random(endian: std.builtin.Endian) CompressedScalar {66pub fn random(io: std.Io, endian: std.builtin.Endian) CompressedScalar {
67 return Scalar.random().toBytes(endian);67 return Scalar.random(io).toBytes(endian);
68}68}
6969
70/// A scalar in unpacked representation.70/// A scalar in unpacked representation.
...@@ -159,10 +159,10 @@ pub const Scalar = struct {...@@ -159,10 +159,10 @@ pub const Scalar = struct {
159 }159 }
160160
161 /// Return a random scalar < L.161 /// Return a random scalar < L.
162 pub fn random() Scalar {162 pub fn random(io: std.Io) Scalar {
163 var s: [64]u8 = undefined;163 var s: [64]u8 = undefined;
164 while (true) {164 while (true) {
165 crypto.random.bytes(&s);165 io.random(&s);
166 const n = Scalar.fromBytes64(s, .little);166 const n = Scalar.fromBytes64(s, .little);
167 if (!n.isZero()) {167 if (!n.isZero()) {
168 return n;168 return n;
lib/std/crypto/pcurves/secp256k1.zig+2-2
...@@ -175,8 +175,8 @@ pub const Secp256k1 = struct {...@@ -175,8 +175,8 @@ pub const Secp256k1 = struct {
175 }175 }
176176
177 /// Return a random point.177 /// Return a random point.
178 pub fn random() Secp256k1 {178 pub fn random(io: std.Io) Secp256k1 {
179 const n = scalar.random(.little);179 const n = scalar.random(io, .little);
180 return basePoint.mul(n, .little) catch unreachable;180 return basePoint.mul(n, .little) catch unreachable;
181 }181 }
182182
lib/std/crypto/pcurves/secp256k1/scalar.zig+4-4
...@@ -68,8 +68,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)...@@ -68,8 +68,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)
68}68}
6969
70/// Return a random scalar70/// Return a random scalar
71pub fn random(endian: std.builtin.Endian) CompressedScalar {71pub fn random(io: std.Io, endian: std.builtin.Endian) CompressedScalar {
72 return Scalar.random().toBytes(endian);72 return Scalar.random(io).toBytes(endian);
73}73}
7474
75/// A scalar in unpacked representation.75/// A scalar in unpacked representation.
...@@ -170,10 +170,10 @@ pub const Scalar = struct {...@@ -170,10 +170,10 @@ pub const Scalar = struct {
170 }170 }
171171
172 /// Return a random scalar < L.172 /// Return a random scalar < L.
173 pub fn random() Scalar {173 pub fn random(io: std.Io) Scalar {
174 var s: [48]u8 = undefined;174 var s: [48]u8 = undefined;
175 while (true) {175 while (true) {
176 crypto.random.bytes(&s);176 io.random(&s);
177 const n = Scalar.fromBytes48(s, .little);177 const n = Scalar.fromBytes48(s, .little);
178 if (!n.isZero()) {178 if (!n.isZero()) {
179 return n;179 return n;
lib/std/crypto/pcurves/tests/p256.zig+11-6
...@@ -5,8 +5,9 @@ const testing = std.testing;...@@ -5,8 +5,9 @@ const testing = std.testing;
5const P256 = @import("../p256.zig").P256;5const P256 = @import("../p256.zig").P256;
66
7test "p256 ECDH key exchange" {7test "p256 ECDH key exchange" {
8 const dha = P256.scalar.random(.little);8 const io = testing.io;
9 const dhb = P256.scalar.random(.little);9 const dha = P256.scalar.random(io, .little);
10 const dhb = P256.scalar.random(io, .little);
10 const dhA = try P256.basePoint.mul(dha, .little);11 const dhA = try P256.basePoint.mul(dha, .little);
11 const dhB = try P256.basePoint.mul(dhb, .little);12 const dhB = try P256.basePoint.mul(dhb, .little);
12 const shareda = try dhA.mul(dhb, .little);13 const shareda = try dhA.mul(dhb, .little);
...@@ -66,28 +67,32 @@ test "p256 test vectors - doubling" {...@@ -66,28 +67,32 @@ test "p256 test vectors - doubling" {
66}67}
6768
68test "p256 compressed sec1 encoding/decoding" {69test "p256 compressed sec1 encoding/decoding" {
69 const p = P256.random();70 const io = testing.io;
71 const p = P256.random(io);
70 const s = p.toCompressedSec1();72 const s = p.toCompressedSec1();
71 const q = try P256.fromSec1(&s);73 const q = try P256.fromSec1(&s);
72 try testing.expect(p.equivalent(q));74 try testing.expect(p.equivalent(q));
73}75}
7476
75test "p256 uncompressed sec1 encoding/decoding" {77test "p256 uncompressed sec1 encoding/decoding" {
76 const p = P256.random();78 const io = testing.io;
79 const p = P256.random(io);
77 const s = p.toUncompressedSec1();80 const s = p.toUncompressedSec1();
78 const q = try P256.fromSec1(&s);81 const q = try P256.fromSec1(&s);
79 try testing.expect(p.equivalent(q));82 try testing.expect(p.equivalent(q));
80}83}
8184
82test "p256 public key is the neutral element" {85test "p256 public key is the neutral element" {
86 const io = testing.io;
83 const n = P256.scalar.Scalar.zero.toBytes(.little);87 const n = P256.scalar.Scalar.zero.toBytes(.little);
84 const p = P256.random();88 const p = P256.random(io);
85 try testing.expectError(error.IdentityElement, p.mul(n, .little));89 try testing.expectError(error.IdentityElement, p.mul(n, .little));
86}90}
8791
88test "p256 public key is the neutral element (public verification)" {92test "p256 public key is the neutral element (public verification)" {
93 const io = testing.io;
89 const n = P256.scalar.Scalar.zero.toBytes(.little);94 const n = P256.scalar.Scalar.zero.toBytes(.little);
90 const p = P256.random();95 const p = P256.random(io);
91 try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));96 try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));
92}97}
9398
lib/std/crypto/pcurves/tests/p384.zig+11-6
...@@ -5,8 +5,9 @@ const testing = std.testing;...@@ -5,8 +5,9 @@ const testing = std.testing;
5const P384 = @import("../p384.zig").P384;5const P384 = @import("../p384.zig").P384;
66
7test "p384 ECDH key exchange" {7test "p384 ECDH key exchange" {
8 const dha = P384.scalar.random(.little);8 const io = testing.io;
9 const dhb = P384.scalar.random(.little);9 const dha = P384.scalar.random(io, .little);
10 const dhb = P384.scalar.random(io, .little);
10 const dhA = try P384.basePoint.mul(dha, .little);11 const dhA = try P384.basePoint.mul(dha, .little);
11 const dhB = try P384.basePoint.mul(dhb, .little);12 const dhB = try P384.basePoint.mul(dhb, .little);
12 const shareda = try dhA.mul(dhb, .little);13 const shareda = try dhA.mul(dhb, .little);
...@@ -67,7 +68,8 @@ test "p384 test vectors - doubling" {...@@ -67,7 +68,8 @@ test "p384 test vectors - doubling" {
67}68}
6869
69test "p384 compressed sec1 encoding/decoding" {70test "p384 compressed sec1 encoding/decoding" {
70 const p = P384.random();71 const io = testing.io;
72 const p = P384.random(io);
71 const s0 = p.toUncompressedSec1();73 const s0 = p.toUncompressedSec1();
72 const s = p.toCompressedSec1();74 const s = p.toCompressedSec1();
73 try testing.expectEqualSlices(u8, s0[1..49], s[1..49]);75 try testing.expectEqualSlices(u8, s0[1..49], s[1..49]);
...@@ -76,21 +78,24 @@ test "p384 compressed sec1 encoding/decoding" {...@@ -76,21 +78,24 @@ test "p384 compressed sec1 encoding/decoding" {
76}78}
7779
78test "p384 uncompressed sec1 encoding/decoding" {80test "p384 uncompressed sec1 encoding/decoding" {
79 const p = P384.random();81 const io = testing.io;
82 const p = P384.random(io);
80 const s = p.toUncompressedSec1();83 const s = p.toUncompressedSec1();
81 const q = try P384.fromSec1(&s);84 const q = try P384.fromSec1(&s);
82 try testing.expect(p.equivalent(q));85 try testing.expect(p.equivalent(q));
83}86}
8487
85test "p384 public key is the neutral element" {88test "p384 public key is the neutral element" {
89 const io = testing.io;
86 const n = P384.scalar.Scalar.zero.toBytes(.little);90 const n = P384.scalar.Scalar.zero.toBytes(.little);
87 const p = P384.random();91 const p = P384.random(io);
88 try testing.expectError(error.IdentityElement, p.mul(n, .little));92 try testing.expectError(error.IdentityElement, p.mul(n, .little));
89}93}
9094
91test "p384 public key is the neutral element (public verification)" {95test "p384 public key is the neutral element (public verification)" {
96 const io = testing.io;
92 const n = P384.scalar.Scalar.zero.toBytes(.little);97 const n = P384.scalar.Scalar.zero.toBytes(.little);
93 const p = P384.random();98 const p = P384.random(io);
94 try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));99 try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));
95}100}
96101
lib/std/crypto/pcurves/tests/secp256k1.zig+14-8
...@@ -5,8 +5,9 @@ const testing = std.testing;...@@ -5,8 +5,9 @@ const testing = std.testing;
5const Secp256k1 = @import("../secp256k1.zig").Secp256k1;5const Secp256k1 = @import("../secp256k1.zig").Secp256k1;
66
7test "secp256k1 ECDH key exchange" {7test "secp256k1 ECDH key exchange" {
8 const dha = Secp256k1.scalar.random(.little);8 const io = testing.io;
9 const dhb = Secp256k1.scalar.random(.little);9 const dha = Secp256k1.scalar.random(io, .little);
10 const dhb = Secp256k1.scalar.random(io, .little);
10 const dhA = try Secp256k1.basePoint.mul(dha, .little);11 const dhA = try Secp256k1.basePoint.mul(dha, .little);
11 const dhB = try Secp256k1.basePoint.mul(dhb, .little);12 const dhB = try Secp256k1.basePoint.mul(dhb, .little);
12 const shareda = try dhA.mul(dhb, .little);13 const shareda = try dhA.mul(dhb, .little);
...@@ -15,8 +16,9 @@ test "secp256k1 ECDH key exchange" {...@@ -15,8 +16,9 @@ test "secp256k1 ECDH key exchange" {
15}16}
1617
17test "secp256k1 ECDH key exchange including public multiplication" {18test "secp256k1 ECDH key exchange including public multiplication" {
18 const dha = Secp256k1.scalar.random(.little);19 const io = testing.io;
19 const dhb = Secp256k1.scalar.random(.little);20 const dha = Secp256k1.scalar.random(io, .little);
21 const dhb = Secp256k1.scalar.random(io, .little);
20 const dhA = try Secp256k1.basePoint.mul(dha, .little);22 const dhA = try Secp256k1.basePoint.mul(dha, .little);
21 const dhB = try Secp256k1.basePoint.mulPublic(dhb, .little);23 const dhB = try Secp256k1.basePoint.mulPublic(dhb, .little);
22 const shareda = try dhA.mul(dhb, .little);24 const shareda = try dhA.mul(dhb, .little);
...@@ -77,28 +79,32 @@ test "secp256k1 test vectors - doubling" {...@@ -77,28 +79,32 @@ test "secp256k1 test vectors - doubling" {
77}79}
7880
79test "secp256k1 compressed sec1 encoding/decoding" {81test "secp256k1 compressed sec1 encoding/decoding" {
80 const p = Secp256k1.random();82 const io = testing.io;
83 const p = Secp256k1.random(io);
81 const s = p.toCompressedSec1();84 const s = p.toCompressedSec1();
82 const q = try Secp256k1.fromSec1(&s);85 const q = try Secp256k1.fromSec1(&s);
83 try testing.expect(p.equivalent(q));86 try testing.expect(p.equivalent(q));
84}87}
8588
86test "secp256k1 uncompressed sec1 encoding/decoding" {89test "secp256k1 uncompressed sec1 encoding/decoding" {
87 const p = Secp256k1.random();90 const io = testing.io;
91 const p = Secp256k1.random(io);
88 const s = p.toUncompressedSec1();92 const s = p.toUncompressedSec1();
89 const q = try Secp256k1.fromSec1(&s);93 const q = try Secp256k1.fromSec1(&s);
90 try testing.expect(p.equivalent(q));94 try testing.expect(p.equivalent(q));
91}95}
9296
93test "secp256k1 public key is the neutral element" {97test "secp256k1 public key is the neutral element" {
98 const io = testing.io;
94 const n = Secp256k1.scalar.Scalar.zero.toBytes(.little);99 const n = Secp256k1.scalar.Scalar.zero.toBytes(.little);
95 const p = Secp256k1.random();100 const p = Secp256k1.random(io);
96 try testing.expectError(error.IdentityElement, p.mul(n, .little));101 try testing.expectError(error.IdentityElement, p.mul(n, .little));
97}102}
98103
99test "secp256k1 public key is the neutral element (public verification)" {104test "secp256k1 public key is the neutral element (public verification)" {
105 const io = testing.io;
100 const n = Secp256k1.scalar.Scalar.zero.toBytes(.little);106 const n = Secp256k1.scalar.Scalar.zero.toBytes(.little);
101 const p = Secp256k1.random();107 const p = Secp256k1.random(io);
102 try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));108 try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));
103}109}
104110
lib/std/crypto/salsa20.zig+19-15
...@@ -533,9 +533,9 @@ pub const SealedBox = struct {...@@ -533,9 +533,9 @@ pub const SealedBox = struct {
533533
534 /// Encrypt a message `m` for a recipient whose public key is `public_key`.534 /// Encrypt a message `m` for a recipient whose public key is `public_key`.
535 /// `c` must be `seal_length` bytes larger than `m`, so that the required metadata can be added.535 /// `c` must be `seal_length` bytes larger than `m`, so that the required metadata can be added.
536 pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) (WeakPublicKeyError || IdentityElementError)!void {536 pub fn seal(io: std.Io, c: []u8, m: []const u8, public_key: [public_length]u8) (WeakPublicKeyError || IdentityElementError)!void {
537 debug.assert(c.len == m.len + seal_length);537 debug.assert(c.len == m.len + seal_length);
538 var ekp = KeyPair.generate();538 var ekp = KeyPair.generate(io);
539 const nonce = createNonce(ekp.public_key, public_key);539 const nonce = createNonce(ekp.public_key, public_key);
540 c[0..public_length].* = ekp.public_key;540 c[0..public_length].* = ekp.public_key;
541 try Box.seal(c[Box.public_length..], m, nonce, public_key, ekp.secret_key);541 try Box.seal(c[Box.public_length..], m, nonce, public_key, ekp.secret_key);
...@@ -573,29 +573,31 @@ test "(x)salsa20" {...@@ -573,29 +573,31 @@ test "(x)salsa20" {
573}573}
574574
575test "xsalsa20poly1305" {575test "xsalsa20poly1305" {
576 const io = std.testing.io;
576 var msg: [100]u8 = undefined;577 var msg: [100]u8 = undefined;
577 var msg2: [msg.len]u8 = undefined;578 var msg2: [msg.len]u8 = undefined;
578 var c: [msg.len]u8 = undefined;579 var c: [msg.len]u8 = undefined;
579 var key: [XSalsa20Poly1305.key_length]u8 = undefined;580 var key: [XSalsa20Poly1305.key_length]u8 = undefined;
580 var nonce: [XSalsa20Poly1305.nonce_length]u8 = undefined;581 var nonce: [XSalsa20Poly1305.nonce_length]u8 = undefined;
581 var tag: [XSalsa20Poly1305.tag_length]u8 = undefined;582 var tag: [XSalsa20Poly1305.tag_length]u8 = undefined;
582 crypto.random.bytes(&msg);583 io.random(&msg);
583 crypto.random.bytes(&key);584 io.random(&key);
584 crypto.random.bytes(&nonce);585 io.random(&nonce);
585586
586 XSalsa20Poly1305.encrypt(c[0..], &tag, msg[0..], "ad", nonce, key);587 XSalsa20Poly1305.encrypt(c[0..], &tag, msg[0..], "ad", nonce, key);
587 try XSalsa20Poly1305.decrypt(msg2[0..], c[0..], tag, "ad", nonce, key);588 try XSalsa20Poly1305.decrypt(msg2[0..], c[0..], tag, "ad", nonce, key);
588}589}
589590
590test "xsalsa20poly1305 secretbox" {591test "xsalsa20poly1305 secretbox" {
592 const io = std.testing.io;
591 var msg: [100]u8 = undefined;593 var msg: [100]u8 = undefined;
592 var msg2: [msg.len]u8 = undefined;594 var msg2: [msg.len]u8 = undefined;
593 var key: [XSalsa20Poly1305.key_length]u8 = undefined;595 var key: [XSalsa20Poly1305.key_length]u8 = undefined;
594 var nonce: [Box.nonce_length]u8 = undefined;596 var nonce: [Box.nonce_length]u8 = undefined;
595 var boxed: [msg.len + Box.tag_length]u8 = undefined;597 var boxed: [msg.len + Box.tag_length]u8 = undefined;
596 crypto.random.bytes(&msg);598 io.random(&msg);
597 crypto.random.bytes(&key);599 io.random(&key);
598 crypto.random.bytes(&nonce);600 io.random(&nonce);
599601
600 SecretBox.seal(boxed[0..], msg[0..], nonce, key);602 SecretBox.seal(boxed[0..], msg[0..], nonce, key);
601 try SecretBox.open(msg2[0..], boxed[0..], nonce, key);603 try SecretBox.open(msg2[0..], boxed[0..], nonce, key);
...@@ -604,15 +606,16 @@ test "xsalsa20poly1305 secretbox" {...@@ -604,15 +606,16 @@ test "xsalsa20poly1305 secretbox" {
604test "xsalsa20poly1305 box" {606test "xsalsa20poly1305 box" {
605 if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/24299607 if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/24299
606608
609 const io = std.testing.io;
607 var msg: [100]u8 = undefined;610 var msg: [100]u8 = undefined;
608 var msg2: [msg.len]u8 = undefined;611 var msg2: [msg.len]u8 = undefined;
609 var nonce: [Box.nonce_length]u8 = undefined;612 var nonce: [Box.nonce_length]u8 = undefined;
610 var boxed: [msg.len + Box.tag_length]u8 = undefined;613 var boxed: [msg.len + Box.tag_length]u8 = undefined;
611 crypto.random.bytes(&msg);614 io.random(&msg);
612 crypto.random.bytes(&nonce);615 io.random(&nonce);
613616
614 const kp1 = Box.KeyPair.generate();617 const kp1 = Box.KeyPair.generate(io);
615 const kp2 = Box.KeyPair.generate();618 const kp2 = Box.KeyPair.generate(io);
616 try Box.seal(boxed[0..], msg[0..], nonce, kp1.public_key, kp2.secret_key);619 try Box.seal(boxed[0..], msg[0..], nonce, kp1.public_key, kp2.secret_key);
617 try Box.open(msg2[0..], boxed[0..], nonce, kp2.public_key, kp1.secret_key);620 try Box.open(msg2[0..], boxed[0..], nonce, kp2.public_key, kp1.secret_key);
618}621}
...@@ -620,13 +623,14 @@ test "xsalsa20poly1305 box" {...@@ -620,13 +623,14 @@ test "xsalsa20poly1305 box" {
620test "xsalsa20poly1305 sealedbox" {623test "xsalsa20poly1305 sealedbox" {
621 if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/24299624 if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/24299
622625
626 const io = std.testing.io;
623 var msg: [100]u8 = undefined;627 var msg: [100]u8 = undefined;
624 var msg2: [msg.len]u8 = undefined;628 var msg2: [msg.len]u8 = undefined;
625 var boxed: [msg.len + SealedBox.seal_length]u8 = undefined;629 var boxed: [msg.len + SealedBox.seal_length]u8 = undefined;
626 crypto.random.bytes(&msg);630 io.random(&msg);
627631
628 const kp = Box.KeyPair.generate();632 const kp = Box.KeyPair.generate(io);
629 try SealedBox.seal(boxed[0..], msg[0..], kp.public_key);633 try SealedBox.seal(io, boxed[0..], msg[0..], kp.public_key);
630 try SealedBox.open(msg2[0..], boxed[0..], kp);634 try SealedBox.open(msg2[0..], boxed[0..], kp);
631}635}
632636
lib/std/crypto/scrypt.zig+5-6
...@@ -20,7 +20,7 @@ const Error = pwhash.Error;...@@ -20,7 +20,7 @@ const Error = pwhash.Error;
2020
21const max_size = math.maxInt(usize);21const max_size = math.maxInt(usize);
22const max_int = max_size >> 1;22const max_int = max_size >> 1;
23const default_salt_len = 32;23pub const default_salt_len = 32;
24const default_hash_len = 32;24const default_hash_len = 32;
25const max_salt_len = 64;25const max_salt_len = 64;
26const max_hash_len = 64;26const max_hash_len = 64;
...@@ -417,10 +417,9 @@ const PhcFormatHasher = struct {...@@ -417,10 +417,9 @@ const PhcFormatHasher = struct {
417 password: []const u8,417 password: []const u8,
418 params: Params,418 params: Params,
419 buf: []u8,419 buf: []u8,
420 /// Filled with cryptographically secure entropy.
421 salt: []const u8,
420 ) HasherError![]const u8 {422 ) HasherError![]const u8 {
421 var salt: [default_salt_len]u8 = undefined;
422 crypto.random.bytes(&salt);
423
424 var hash: [default_hash_len]u8 = undefined;423 var hash: [default_hash_len]u8 = undefined;
425 try kdf(allocator, &hash, password, &salt, params);424 try kdf(allocator, &hash, password, &salt, params);
426425
...@@ -466,9 +465,9 @@ const CryptFormatHasher = struct {...@@ -466,9 +465,9 @@ const CryptFormatHasher = struct {
466 password: []const u8,465 password: []const u8,
467 params: Params,466 params: Params,
468 buf: []u8,467 buf: []u8,
468 /// Filled with cryptographically secure entropy.
469 salt_bin: []const u8,
469 ) HasherError![]const u8 {470 ) HasherError![]const u8 {
470 var salt_bin: [default_salt_len]u8 = undefined;
471 crypto.random.bytes(&salt_bin);
472 const salt = crypt_format.saltFromBin(salt_bin.len, salt_bin);471 const salt = crypt_format.saltFromBin(salt_bin.len, salt_bin);
473472
474 var hash: [default_hash_len]u8 = undefined;473 var hash: [default_hash_len]u8 = undefined;
lib/std/crypto/timing_safe.zig+12-11
...@@ -180,24 +180,24 @@ pub fn declassify(ptr: anytype) void {...@@ -180,24 +180,24 @@ pub fn declassify(ptr: anytype) void {
180}180}
181181
182test eql {182test eql {
183 const random = std.crypto.random;183 const io = std.testing.io;
184 const expect = std.testing.expect;184 const expect = std.testing.expect;
185 var a: [100]u8 = undefined;185 var a: [100]u8 = undefined;
186 var b: [100]u8 = undefined;186 var b: [100]u8 = undefined;
187 random.bytes(a[0..]);187 io.random(&a);
188 random.bytes(b[0..]);188 io.random(&b);
189 try expect(!eql([100]u8, a, b));189 try expect(!eql([100]u8, a, b));
190 a = b;190 a = b;
191 try expect(eql([100]u8, a, b));191 try expect(eql([100]u8, a, b));
192}192}
193193
194test "eql (vectors)" {194test "eql (vectors)" {
195 const random = std.crypto.random;195 const io = std.testing.io;
196 const expect = std.testing.expect;196 const expect = std.testing.expect;
197 var a: [100]u8 = undefined;197 var a: [100]u8 = undefined;
198 var b: [100]u8 = undefined;198 var b: [100]u8 = undefined;
199 random.bytes(a[0..]);199 io.random(&a);
200 random.bytes(b[0..]);200 io.random(&b);
201 const v1: @Vector(100, u8) = a;201 const v1: @Vector(100, u8) = a;
202 const v2: @Vector(100, u8) = b;202 const v2: @Vector(100, u8) = b;
203 try expect(!eql(@Vector(100, u8), v1, v2));203 try expect(!eql(@Vector(100, u8), v1, v2));
...@@ -220,9 +220,10 @@ test compare {...@@ -220,9 +220,10 @@ test compare {
220}220}
221221
222test "add and sub" {222test "add and sub" {
223 const io = std.testing.io;
224
223 const expectEqual = std.testing.expectEqual;225 const expectEqual = std.testing.expectEqual;
224 const expectEqualSlices = std.testing.expectEqualSlices;226 const expectEqualSlices = std.testing.expectEqualSlices;
225 const random = std.crypto.random;
226 const len = 32;227 const len = 32;
227 var a: [len]u8 = undefined;228 var a: [len]u8 = undefined;
228 var b: [len]u8 = undefined;229 var b: [len]u8 = undefined;
...@@ -230,8 +231,8 @@ test "add and sub" {...@@ -230,8 +231,8 @@ test "add and sub" {
230 const zero = [_]u8{0} ** len;231 const zero = [_]u8{0} ** len;
231 var iterations: usize = 100;232 var iterations: usize = 100;
232 while (iterations != 0) : (iterations -= 1) {233 while (iterations != 0) : (iterations -= 1) {
233 random.bytes(&a);234 io.random(&a);
234 random.bytes(&b);235 io.random(&b);
235 const endian = if (iterations % 2 == 0) Endian.big else Endian.little;236 const endian = if (iterations % 2 == 0) Endian.big else Endian.little;
236 _ = sub(u8, &a, &b, &c, endian); // a-b237 _ = sub(u8, &a, &b, &c, endian); // a-b
237 _ = add(u8, &c, &b, &c, endian); // (a-b)+b238 _ = add(u8, &c, &b, &c, endian); // (a-b)+b
...@@ -243,11 +244,11 @@ test "add and sub" {...@@ -243,11 +244,11 @@ test "add and sub" {
243}244}
244245
245test classify {246test classify {
246 const random = std.crypto.random;247 const io = std.testing.io;
247 const expect = std.testing.expect;248 const expect = std.testing.expect;
248249
249 var secret: [32]u8 = undefined;250 var secret: [32]u8 = undefined;
250 random.bytes(&secret);251 io.random(&secret);
251252
252 // Input of the hash function is marked as secret253 // Input of the hash function is marked as secret
253 classify(&secret);254 classify(&secret);
lib/std/crypto/tlcsprng.zig deleted-169
...@@ -1,169 +0,0 @@
1//! Thread-local cryptographically secure pseudo-random number generator.
2//! This file has public declarations that are intended to be used internally
3//! by the standard library; this namespace is not intended to be exposed
4//! directly to standard library users.
5
6const std = @import("std");
7const builtin = @import("builtin");
8const mem = std.mem;
9const native_os = builtin.os.tag;
10const posix = std.posix;
11
12/// We use this as a layer of indirection because global const pointers cannot
13/// point to thread-local variables.
14pub const interface: std.Random = .{
15 .ptr = undefined,
16 .fillFn = tlsCsprngFill,
17};
18
19const os_has_fork = @TypeOf(posix.fork) != void;
20const os_has_arc4random = builtin.link_libc and (@TypeOf(std.c.arc4random_buf) != void);
21const want_fork_safety = os_has_fork and !os_has_arc4random and std.options.crypto_fork_safety;
22const maybe_have_wipe_on_fork = builtin.os.isAtLeast(.linux, .{
23 .major = 4,
24 .minor = 14,
25 .patch = 0,
26}) orelse true;
27
28const Rng = std.Random.DefaultCsprng;
29
30const Context = struct {
31 init_state: enum(u8) { uninitialized = 0, initialized, failed },
32 rng: Rng,
33};
34
35var install_atfork_handler = std.once(struct {
36 // Install the global handler only once.
37 // The same handler is shared among threads and is inherinted by fork()-ed
38 // processes.
39 fn do() void {
40 const r = std.c.pthread_atfork(null, null, childAtForkHandler);
41 std.debug.assert(r == 0);
42 }
43}.do);
44
45threadlocal var wipe_mem: []align(std.heap.page_size_min) u8 = &[_]u8{};
46
47fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
48 if (os_has_arc4random) {
49 // arc4random is already a thread-local CSPRNG.
50 return std.c.arc4random_buf(buffer.ptr, buffer.len);
51 }
52 // Allow applications to decide they would prefer to have every call to
53 // std.crypto.random always make an OS syscall, rather than rely on an
54 // application implementation of a CSPRNG.
55 if (std.options.crypto_always_getrandom) {
56 return std.options.cryptoRandomSeed(buffer);
57 }
58
59 if (wipe_mem.len == 0) {
60 // Not initialized yet.
61 if (want_fork_safety and maybe_have_wipe_on_fork) {
62 // Allocate a per-process page, madvise operates with page
63 // granularity.
64 wipe_mem = posix.mmap(
65 null,
66 @sizeOf(Context),
67 posix.PROT.READ | posix.PROT.WRITE,
68 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
69 -1,
70 0,
71 ) catch {
72 // Could not allocate memory for the local state, fall back to
73 // the OS syscall.
74 return std.options.cryptoRandomSeed(buffer);
75 };
76 // The memory is already zero-initialized.
77 } else {
78 // Use a static thread-local buffer.
79 const S = struct {
80 threadlocal var buf: Context align(std.heap.page_size_min) = .{
81 .init_state = .uninitialized,
82 .rng = undefined,
83 };
84 };
85 wipe_mem = mem.asBytes(&S.buf);
86 }
87 }
88 const ctx: *Context = @ptrCast(wipe_mem.ptr);
89
90 switch (ctx.init_state) {
91 .uninitialized => {
92 if (!want_fork_safety) {
93 return initAndFill(buffer);
94 }
95
96 if (maybe_have_wipe_on_fork) wof: {
97 // Qemu user-mode emulation ignores any valid/invalid madvise
98 // hint and returns success. Check if this is the case by
99 // passing bogus parameters, we expect EINVAL as result.
100 if (posix.madvise(wipe_mem.ptr, 0, 0xffffffff)) |_| {
101 break :wof;
102 } else |_| {}
103
104 if (posix.madvise(wipe_mem.ptr, wipe_mem.len, posix.MADV.WIPEONFORK)) |_| {
105 return initAndFill(buffer);
106 } else |_| {}
107 }
108
109 if (std.Thread.use_pthreads) {
110 return setupPthreadAtforkAndFill(buffer);
111 }
112
113 // Since we failed to set up fork safety, we fall back to always
114 // calling getrandom every time.
115 ctx.init_state = .failed;
116 return std.options.cryptoRandomSeed(buffer);
117 },
118 .initialized => {
119 return fillWithCsprng(buffer);
120 },
121 .failed => {
122 if (want_fork_safety) {
123 return std.options.cryptoRandomSeed(buffer);
124 } else {
125 unreachable;
126 }
127 },
128 }
129}
130
131fn setupPthreadAtforkAndFill(buffer: []u8) void {
132 install_atfork_handler.call();
133 return initAndFill(buffer);
134}
135
136fn childAtForkHandler() callconv(.c) void {
137 // The atfork handler is global, this function may be called after
138 // fork()-ing threads that never initialized the CSPRNG context.
139 if (wipe_mem.len == 0) return;
140 std.crypto.secureZero(u8, wipe_mem);
141}
142
143fn fillWithCsprng(buffer: []u8) void {
144 const ctx: *Context = @ptrCast(wipe_mem.ptr);
145 return ctx.rng.fill(buffer);
146}
147
148pub fn defaultRandomSeed(buffer: []u8) void {
149 posix.getrandom(buffer) catch @panic("getrandom() failed to provide entropy");
150}
151
152fn initAndFill(buffer: []u8) void {
153 var seed: [Rng.secret_seed_length]u8 = undefined;
154 // Because we panic on getrandom() failing, we provide the opportunity
155 // to override the default seed function. This also makes
156 // `std.crypto.random` available on freestanding targets, provided that
157 // the `std.options.cryptoRandomSeed` function is provided.
158 std.options.cryptoRandomSeed(&seed);
159
160 const ctx: *Context = @ptrCast(wipe_mem.ptr);
161 ctx.rng = Rng.init(seed);
162 std.crypto.secureZero(u8, &seed);
163
164 // This is at the end so that accidental recursive dependencies result
165 // in stack overflows instead of invalid random data.
166 ctx.init_state = .initialized;
167
168 return fillWithCsprng(buffer);
169}
lib/std/crypto/tls/Client.zig+9-7
...@@ -109,7 +109,7 @@ pub const Options = struct {...@@ -109,7 +109,7 @@ pub const Options = struct {
109 read_buffer: []u8,109 read_buffer: []u8,
110 /// Cryptographically secure random bytes. The pointer is not captured; data is only110 /// Cryptographically secure random bytes. The pointer is not captured; data is only
111 /// read during `init`.111 /// read during `init`.
112 entropy: *const [176]u8,112 entropy: *const [entropy_len]u8,
113 /// Current time according to the wall clock / calendar, in seconds.113 /// Current time according to the wall clock / calendar, in seconds.
114 realtime_now_seconds: i64,114 realtime_now_seconds: i64,
115115
...@@ -130,6 +130,8 @@ pub const Options = struct {...@@ -130,6 +130,8 @@ pub const Options = struct {
130 allow_truncation_attacks: bool = false,130 allow_truncation_attacks: bool = false,
131 /// Populated when `error.TlsAlert` is returned from `init`.131 /// Populated when `error.TlsAlert` is returned from `init`.
132 alert: ?*tls.Alert = null,132 alert: ?*tls.Alert = null,
133
134 pub const entropy_len = 240;
133};135};
134136
135const InitError = error{137const InitError = error{
...@@ -200,7 +202,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client...@@ -200,7 +202,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
200 var server_hello_rand: [32]u8 = undefined;202 var server_hello_rand: [32]u8 = undefined;
201 const legacy_session_id = options.entropy[32..64].*;203 const legacy_session_id = options.entropy[32..64].*;
202204
203 var key_share = KeyShare.init(options.entropy[64..176].*) catch |err| switch (err) {205 var key_share = KeyShare.init(options.entropy[64..240]) catch |err| switch (err) {
204 // Only possible to happen if the seed is all zeroes.206 // Only possible to happen if the seed is all zeroes.
205 error.IdentityElement => return error.InsufficientEntropy,207 error.IdentityElement => return error.InsufficientEntropy,
206 };208 };
...@@ -1330,12 +1332,12 @@ const KeyShare = struct {...@@ -1330,12 +1332,12 @@ const KeyShare = struct {
1330 crypto.dh.X25519.shared_length,1332 crypto.dh.X25519.shared_length,
1331 );1333 );
13321334
1333 fn init(seed: [112]u8) error{IdentityElement}!KeyShare {1335 fn init(seed: *const [176]u8) error{IdentityElement}!KeyShare {
1334 return .{1336 return .{
1335 .ml_kem768_kp = .generate(),1337 .ml_kem768_kp = try .generateDeterministic(seed[0..64].*),
1336 .secp256r1_kp = try .generateDeterministic(seed[0..32].*),1338 .secp256r1_kp = try .generateDeterministic(seed[64..96].*),
1337 .secp384r1_kp = try .generateDeterministic(seed[32..80].*),1339 .secp384r1_kp = try .generateDeterministic(seed[96..144].*),
1338 .x25519_kp = try .generateDeterministic(seed[80..112].*),1340 .x25519_kp = try .generateDeterministic(seed[144..176].*),
1339 .sk_buf = undefined,1341 .sk_buf = undefined,
1340 .sk_len = 0,1342 .sk_len = 0,
1341 };1343 };
lib/std/fs/test.zig+17-3
...@@ -1022,8 +1022,7 @@ test "Dir.rename directory onto non-empty dir" {...@@ -1022,8 +1022,7 @@ test "Dir.rename directory onto non-empty dir" {
1022 file.close(io);1022 file.close(io);
1023 target_dir.close(io);1023 target_dir.close(io);
10241024
1025 // Rename should fail with PathAlreadyExists if target_dir is non-empty1025 try expectError(error.DirNotEmpty, ctx.dir.rename(test_dir_path, ctx.dir, target_dir_path, io));
1026 try expectError(error.PathAlreadyExists, ctx.dir.rename(test_dir_path, ctx.dir, target_dir_path, io));
10271026
1028 // Ensure the directory was not renamed1027 // Ensure the directory was not renamed
1029 var dir = try ctx.dir.openDir(io, test_dir_path, .{});1028 var dir = try ctx.dir.openDir(io, test_dir_path, .{});
...@@ -1651,6 +1650,21 @@ test "AtomicFile" {...@@ -1651,6 +1650,21 @@ test "AtomicFile" {
1651 \\ this is a test file1650 \\ this is a test file
1652 ;1651 ;
16531652
1653 // link() succeeds with no file already present
1654 {
1655 var af = try ctx.dir.createFileAtomic(io, test_out_file, .{ .replace = false });
1656 defer af.deinit(io);
1657 try af.file.writeStreamingAll(io, test_content);
1658 try af.link(io);
1659 }
1660 // link() returns error.PathAlreadyExists if file already present
1661 {
1662 var af = try ctx.dir.createFileAtomic(io, test_out_file, .{ .replace = false });
1663 defer af.deinit(io);
1664 try af.file.writeStreamingAll(io, test_content);
1665 try expectError(error.PathAlreadyExists, af.link(io));
1666 }
1667 // replace() succeeds if file already present
1654 {1668 {
1655 var af = try ctx.dir.createFileAtomic(io, test_out_file, .{ .replace = true });1669 var af = try ctx.dir.createFileAtomic(io, test_out_file, .{ .replace = true });
1656 defer af.deinit(io);1670 defer af.deinit(io);
...@@ -1761,7 +1775,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {...@@ -1761,7 +1775,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
1761 const io = testing.io;1775 const io = testing.io;
17621776
1763 var random_bytes: [12]u8 = undefined;1777 var random_bytes: [12]u8 = undefined;
1764 std.crypto.random.bytes(&random_bytes);1778 io.random(&random_bytes);
17651779
1766 var random_b64: [std.fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined;1780 var random_b64: [std.fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined;
1767 _ = std.fs.base64_encoder.encode(&random_b64, &random_bytes);1781 _ = std.fs.base64_encoder.encode(&random_b64, &random_bytes);
lib/std/http/Client.zig+2-2
...@@ -321,8 +321,8 @@ pub const Connection = struct {...@@ -321,8 +321,8 @@ pub const Connection = struct {
321 assert(base.ptr + alloc_len == socket_read_buffer.ptr + socket_read_buffer.len);321 assert(base.ptr + alloc_len == socket_read_buffer.ptr + socket_read_buffer.len);
322 @memcpy(host_buffer, remote_host.bytes);322 @memcpy(host_buffer, remote_host.bytes);
323 const tls: *Tls = @ptrCast(base);323 const tls: *Tls = @ptrCast(base);
324 var random_buffer: [176]u8 = undefined;324 var random_buffer: [std.crypto.tls.Client.Options.entropy_len]u8 = undefined;
325 std.crypto.random.bytes(&random_buffer);325 io.random(&random_buffer);
326 tls.* = .{326 tls.* = .{
327 .connection = .{327 .connection = .{
328 .client = client,328 .client = client,
lib/std/os/linux.zig+26-4
...@@ -501,6 +501,15 @@ pub const O = switch (native_arch) {...@@ -501,6 +501,15 @@ pub const O = switch (native_arch) {
501 else => @compileError("missing std.os.linux.O constants for this architecture"),501 else => @compileError("missing std.os.linux.O constants for this architecture"),
502};502};
503503
504pub const RENAME = packed struct(u32) {
505 /// Cannot be set together with `EXCHANGE`.
506 NOREPLACE: bool = false,
507 /// Cannot be set together with `NOREPLACE`.
508 EXCHANGE: bool = false,
509 WHITEOUT: bool = false,
510 _: u29 = 0,
511};
512
504/// Set by startup code, used by `getauxval`.513/// Set by startup code, used by `getauxval`.
505pub var elf_aux_maybe: ?[*]std.elf.Auxv = null;514pub var elf_aux_maybe: ?[*]std.elf.Auxv = null;
506515
...@@ -1346,9 +1355,22 @@ pub fn rename(old: [*:0]const u8, new: [*:0]const u8) usize {...@@ -1346,9 +1355,22 @@ pub fn rename(old: [*:0]const u8, new: [*:0]const u8) usize {
1346 if (@hasField(SYS, "rename")) {1355 if (@hasField(SYS, "rename")) {
1347 return syscall2(.rename, @intFromPtr(old), @intFromPtr(new));1356 return syscall2(.rename, @intFromPtr(old), @intFromPtr(new));
1348 } else if (@hasField(SYS, "renameat")) {1357 } else if (@hasField(SYS, "renameat")) {
1349 return syscall4(.renameat, @as(usize, @bitCast(@as(isize, AT.FDCWD))), @intFromPtr(old), @as(usize, @bitCast(@as(isize, AT.FDCWD))), @intFromPtr(new));1358 return syscall4(
1359 .renameat,
1360 @as(usize, @bitCast(@as(isize, AT.FDCWD))),
1361 @intFromPtr(old),
1362 @as(usize, @bitCast(@as(isize, AT.FDCWD))),
1363 @intFromPtr(new),
1364 );
1350 } else {1365 } else {
1351 return syscall5(.renameat2, @as(usize, @bitCast(@as(isize, AT.FDCWD))), @intFromPtr(old), @as(usize, @bitCast(@as(isize, AT.FDCWD))), @intFromPtr(new), 0);1366 return syscall5(
1367 .renameat2,
1368 @as(usize, @bitCast(@as(isize, AT.FDCWD))),
1369 @intFromPtr(old),
1370 @as(usize, @bitCast(@as(isize, AT.FDCWD))),
1371 @intFromPtr(new),
1372 0,
1373 );
1352 }1374 }
1353}1375}
13541376
...@@ -1373,14 +1395,14 @@ pub fn renameat(oldfd: i32, oldpath: [*:0]const u8, newfd: i32, newpath: [*:0]co...@@ -1373,14 +1395,14 @@ pub fn renameat(oldfd: i32, oldpath: [*:0]const u8, newfd: i32, newpath: [*:0]co
1373 }1395 }
1374}1396}
13751397
1376pub fn renameat2(oldfd: i32, oldpath: [*:0]const u8, newfd: i32, newpath: [*:0]const u8, flags: u32) usize {1398pub fn renameat2(oldfd: i32, oldpath: [*:0]const u8, newfd: i32, newpath: [*:0]const u8, flags: RENAME) usize {
1377 return syscall5(1399 return syscall5(
1378 .renameat2,1400 .renameat2,
1379 @as(usize, @bitCast(@as(isize, oldfd))),1401 @as(usize, @bitCast(@as(isize, oldfd))),
1380 @intFromPtr(oldpath),1402 @intFromPtr(oldpath),
1381 @as(usize, @bitCast(@as(isize, newfd))),1403 @as(usize, @bitCast(@as(isize, newfd))),
1382 @intFromPtr(newpath),1404 @intFromPtr(newpath),
1383 flags,1405 @as(u32, @bitCast(flags)),
1384 );1406 );
1385}1407}
13861408
lib/std/os/linux/tls.zig+5-7
...@@ -531,13 +531,11 @@ pub fn prepareArea(area: []u8) usize {...@@ -531,13 +531,11 @@ pub fn prepareArea(area: []u8) usize {
531 };531 };
532}532}
533533
534/// The main motivation for the size chosen here is that this is how much ends up being requested for534/// The main motivation for the size chosen here is to be larger than total
535/// the thread-local variables of the `std.crypto.random` implementation. I'm not sure why it ends up535/// amount of thread-local variables for most programs. Putting this allocation
536/// being so much; the struct itself is only 64 bytes. I think it has to do with being page-aligned536/// in the ELF like this is equivalent to moving the `mmap` call below into the
537/// and LLVM or LLD is not smart enough to lay out the TLS data in a space-conserving way. Anyway, I537/// kernel, avoiding syscall overhead.
538/// think it's fine because it's less than 3 pages of memory, and putting it in the ELF like this is538var main_thread_area_buffer: [0x1000]u8 align(page_size_min) = undefined;
539/// equivalent to moving the `mmap` call below into the kernel, avoiding syscall overhead.
540var main_thread_area_buffer: [0x2100]u8 align(page_size_min) = undefined;
541539
542/// Computes the layout of the static TLS area, allocates the area, initializes all of its fields,540/// Computes the layout of the static TLS area, allocates the area, initializes all of its fields,
543/// and assigns the architecture-specific value to the TP register.541/// and assigns the architecture-specific value to the TP register.
lib/std/os/windows.zig+2-58
...@@ -2647,62 +2647,6 @@ pub fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInfor...@@ -2647,62 +2647,6 @@ pub fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInfor
2647 }2647 }
2648}2648}
26492649
2650/// An alternate implementation of ProcessPrng from bcryptprimitives.dll
2651/// This one has the following differences:
2652/// * does not heap allocate `buffer`
2653/// * does not introduce a dependency on bcryptprimitives.dll, which apparently
2654/// runs a test suite every time it is loaded
2655/// * reads buffer.len bytes from "\\Device\\CNG" rather than seeding a per-CPU
2656/// AES csprng with 48 bytes.
2657pub fn ProcessPrng(buffer: []u8) error{Unexpected}!void {
2658 const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' };
2659 var nt_name: UNICODE_STRING = .{
2660 .Length = device_path.len * 2,
2661 .MaximumLength = 0,
2662 .Buffer = @constCast(&device_path),
2663 };
2664 var cng_device: HANDLE = undefined;
2665 var io_status_block: IO_STATUS_BLOCK = undefined;
2666 switch (ntdll.NtOpenFile(
2667 &cng_device,
2668 .{
2669 .STANDARD = .{ .SYNCHRONIZE = true },
2670 .SPECIFIC = .{ .FILE = .{ .READ_DATA = true } },
2671 },
2672 &.{
2673 .Length = @sizeOf(OBJECT_ATTRIBUTES),
2674 .RootDirectory = null,
2675 .ObjectName = &nt_name,
2676 .Attributes = .{},
2677 .SecurityDescriptor = null,
2678 .SecurityQualityOfService = null,
2679 },
2680 &io_status_block,
2681 .VALID_FLAGS,
2682 .{ .IO = .SYNCHRONOUS_NONALERT },
2683 )) {
2684 .SUCCESS => {},
2685 .OBJECT_NAME_NOT_FOUND => return error.Unexpected, // Observed on wine 10.0
2686 else => |status| return unexpectedStatus(status),
2687 }
2688 defer _ = ntdll.NtClose(cng_device);
2689 switch (ntdll.NtDeviceIoControlFile(
2690 cng_device,
2691 null,
2692 null,
2693 null,
2694 &io_status_block,
2695 IOCTL.KSEC.GEN_RANDOM,
2696 null,
2697 0,
2698 buffer.ptr,
2699 @intCast(buffer.len),
2700 )) {
2701 .SUCCESS => {},
2702 else => |status| return unexpectedStatus(status),
2703 }
2704}
2705
2706pub const WaitForSingleObjectError = error{2650pub const WaitForSingleObjectError = error{
2707 WaitAbandoned,2651 WaitAbandoned,
2708 WaitTimeOut,2652 WaitTimeOut,
...@@ -3250,7 +3194,7 @@ pub const RenameError = error{...@@ -3250,7 +3194,7 @@ pub const RenameError = error{
3250 NetworkNotFound,3194 NetworkNotFound,
3251 AntivirusInterference,3195 AntivirusInterference,
3252 BadPathName,3196 BadPathName,
3253 RenameAcrossMountPoints,3197 CrossDevice,
3254} || UnexpectedError;3198} || UnexpectedError;
32553199
3256pub fn RenameFile(3200pub fn RenameFile(
...@@ -3351,7 +3295,7 @@ pub fn RenameFile(...@@ -3351,7 +3295,7 @@ pub fn RenameFile(
3351 .ACCESS_DENIED => return error.AccessDenied,3295 .ACCESS_DENIED => return error.AccessDenied,
3352 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,3296 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
3353 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,3297 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
3354 .NOT_SAME_DEVICE => return error.RenameAcrossMountPoints,3298 .NOT_SAME_DEVICE => return error.CrossDevice,
3355 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,3299 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
3356 .DIRECTORY_NOT_EMPTY => return error.PathAlreadyExists,3300 .DIRECTORY_NOT_EMPTY => return error.PathAlreadyExists,
3357 .FILE_IS_A_DIRECTORY => return error.IsDir,3301 .FILE_IS_A_DIRECTORY => return error.IsDir,
lib/std/posix.zig+2-103
...@@ -361,107 +361,6 @@ pub fn reboot(cmd: RebootCommand) RebootError!void {...@@ -361,107 +361,6 @@ pub fn reboot(cmd: RebootCommand) RebootError!void {
361 }361 }
362}362}
363363
364pub const GetRandomError = OpenError;
365
366/// Obtain a series of random bytes. These bytes can be used to seed user-space
367/// random number generators or for cryptographic purposes.
368/// When linking against libc, this calls the
369/// appropriate OS-specific library call. Otherwise it uses the zig standard
370/// library implementation.
371pub fn getrandom(buffer: []u8) GetRandomError!void {
372 if (native_os == .windows) {
373 return windows.ProcessPrng(buffer);
374 }
375 if (builtin.link_libc and @TypeOf(system.arc4random_buf) != void) {
376 system.arc4random_buf(buffer.ptr, buffer.len);
377 return;
378 }
379 if (native_os == .wasi) switch (wasi.random_get(buffer.ptr, buffer.len)) {
380 .SUCCESS => return,
381 else => |err| return unexpectedErrno(err),
382 };
383 if (@TypeOf(system.getrandom) != void) {
384 var buf = buffer;
385 const use_c = native_os != .linux or
386 std.c.versionCheck(if (builtin.abi.isAndroid()) .{ .major = 28, .minor = 0, .patch = 0 } else .{ .major = 2, .minor = 25, .patch = 0 });
387
388 while (buf.len != 0) {
389 const num_read: usize, const err = if (use_c) res: {
390 const rc = std.c.getrandom(buf.ptr, buf.len, 0);
391 break :res .{ @bitCast(rc), errno(rc) };
392 } else res: {
393 const rc = linux.getrandom(buf.ptr, buf.len, 0);
394 break :res .{ rc, linux.errno(rc) };
395 };
396
397 switch (err) {
398 .SUCCESS => buf = buf[num_read..],
399 .INVAL => unreachable,
400 .FAULT => unreachable,
401 .INTR => continue,
402 else => return unexpectedErrno(err),
403 }
404 }
405 return;
406 }
407 if (native_os == .emscripten) {
408 const err = errno(std.c.getentropy(buffer.ptr, buffer.len));
409 switch (err) {
410 .SUCCESS => return,
411 else => return unexpectedErrno(err),
412 }
413 }
414 return getRandomBytesDevURandom(buffer);
415}
416
417fn getRandomBytesDevURandom(buf: []u8) GetRandomError!void {
418 const fd = try openZ("/dev/urandom", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
419 defer close(fd);
420
421 switch (native_os) {
422 .linux => {
423 var stx = std.mem.zeroes(linux.Statx);
424 const rc = linux.statx(
425 fd,
426 "",
427 linux.AT.EMPTY_PATH,
428 .{ .TYPE = true },
429 &stx,
430 );
431 switch (errno(rc)) {
432 .SUCCESS => {},
433 .ACCES => unreachable,
434 .BADF => unreachable,
435 .FAULT => unreachable,
436 .INVAL => unreachable,
437 .LOOP => unreachable,
438 .NAMETOOLONG => unreachable,
439 .NOENT => unreachable,
440 .NOMEM => return error.SystemResources,
441 .NOTDIR => unreachable,
442 else => |err| return unexpectedErrno(err),
443 }
444 if (!S.ISCHR(stx.mode)) {
445 return error.NoDevice;
446 }
447 },
448 else => {
449 const st = fstat(fd) catch |err| switch (err) {
450 error.Streaming => return error.NoDevice,
451 else => |e| return e,
452 };
453 if (!S.ISCHR(st.mode)) {
454 return error.NoDevice;
455 }
456 },
457 }
458
459 var i: usize = 0;
460 while (i < buf.len) {
461 i += read(fd, buf[i..]) catch return error.Unexpected;
462 }
463}
464
465pub const RaiseError = UnexpectedError;364pub const RaiseError = UnexpectedError;
466365
467pub fn raise(sig: SIG) RaiseError!void {366pub fn raise(sig: SIG) RaiseError!void {
...@@ -1695,7 +1594,7 @@ pub const FanotifyMarkError = error{...@@ -1695,7 +1594,7 @@ pub const FanotifyMarkError = error{
1695 NotDir,1594 NotDir,
1696 OperationUnsupported,1595 OperationUnsupported,
1697 PermissionDenied,1596 PermissionDenied,
1698 NotSameFileSystem,1597 CrossDevice,
1699 NameTooLong,1598 NameTooLong,
1700} || UnexpectedError;1599} || UnexpectedError;
17011600
...@@ -1735,7 +1634,7 @@ pub fn fanotify_markZ(...@@ -1735,7 +1634,7 @@ pub fn fanotify_markZ(
1735 .NOTDIR => return error.NotDir,1634 .NOTDIR => return error.NotDir,
1736 .OPNOTSUPP => return error.OperationUnsupported,1635 .OPNOTSUPP => return error.OperationUnsupported,
1737 .PERM => return error.PermissionDenied,1636 .PERM => return error.PermissionDenied,
1738 .XDEV => return error.NotSameFileSystem,1637 .XDEV => return error.CrossDevice,
1739 else => |err| return unexpectedErrno(err),1638 else => |err| return unexpectedErrno(err),
1740 }1639 }
1741}1640}
lib/std/posix/test.zig-10
...@@ -33,16 +33,6 @@ test "check WASI CWD" {...@@ -33,16 +33,6 @@ test "check WASI CWD" {
33 }33 }
34}34}
3535
36test "getrandom" {
37 var buf_a: [50]u8 = undefined;
38 var buf_b: [50]u8 = undefined;
39 try posix.getrandom(&buf_a);
40 try posix.getrandom(&buf_b);
41 // If this test fails the chance is significantly higher that there is a bug than
42 // that two sets of 50 bytes were equal.
43 try expect(!mem.eql(u8, &buf_a, &buf_b));
44}
45
46test "getuid" {36test "getuid" {
47 if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;37 if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;
48 _ = posix.getuid();38 _ = posix.getuid();
lib/std/std.zig-6
...@@ -137,12 +137,6 @@ pub const Options = struct {...@@ -137,12 +137,6 @@ pub const Options = struct {
137137
138 fmt_max_depth: usize = fmt.default_max_depth,138 fmt_max_depth: usize = fmt.default_max_depth,
139139
140 cryptoRandomSeed: fn (buffer: []u8) void = @import("crypto/tlcsprng.zig").defaultRandomSeed,
141
142 crypto_always_getrandom: bool = false,
143
144 crypto_fork_safety: bool = true,
145
146 /// By default, std.http.Client will support HTTPS connections. Set this option to `true` to140 /// By default, std.http.Client will support HTTPS connections. Set this option to `true` to
147 /// disable TLS support.141 /// disable TLS support.
148 ///142 ///
lib/std/testing.zig+1-1
...@@ -631,7 +631,7 @@ pub const TmpDir = struct {...@@ -631,7 +631,7 @@ pub const TmpDir = struct {
631pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir {631pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir {
632 comptime assert(builtin.is_test);632 comptime assert(builtin.is_test);
633 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;633 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
634 std.crypto.random.bytes(&random_bytes);634 io.random(&random_bytes);
635 var sub_path: [TmpDir.sub_path_len]u8 = undefined;635 var sub_path: [TmpDir.sub_path_len]u8 = undefined;
636 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);636 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
637637
src/Compilation.zig+14-5
...@@ -2942,7 +2942,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE...@@ -2942,7 +2942,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
2942 .none => |none| {2942 .none => |none| {
2943 assert(none.tmp_artifact_directory == null);2943 assert(none.tmp_artifact_directory == null);
2944 none.tmp_artifact_directory = d: {2944 none.tmp_artifact_directory = d: {
2945 tmp_dir_rand_int = std.crypto.random.int(u64);2945 io.random(@ptrCast(&tmp_dir_rand_int));
2946 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);2946 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2947 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});2947 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
2948 const handle = comp.dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{}) catch |err| {2948 const handle = comp.dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{}) catch |err| {
...@@ -3023,7 +3023,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE...@@ -3023,7 +3023,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
30233023
3024 // Compile the artifacts to a temporary directory.3024 // Compile the artifacts to a temporary directory.
3025 whole.tmp_artifact_directory = d: {3025 whole.tmp_artifact_directory = d: {
3026 tmp_dir_rand_int = std.crypto.random.int(u64);3026 io.random(@ptrCast(&tmp_dir_rand_int));
3027 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);3027 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
3028 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});3028 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
3029 const handle = comp.dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{}) catch |err| {3029 const handle = comp.dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{}) catch |err| {
...@@ -3460,7 +3460,7 @@ fn renameTmpIntoCache(...@@ -3460,7 +3460,7 @@ fn renameTmpIntoCache(
3460 },3460 },
3461 else => return error.AccessDenied,3461 else => return error.AccessDenied,
3462 },3462 },
3463 error.PathAlreadyExists => {3463 error.DirNotEmpty => {
3464 try cache_directory.handle.deleteTree(io, o_sub_path);3464 try cache_directory.handle.deleteTree(io, o_sub_path);
3465 continue;3465 continue;
3466 },3466 },
...@@ -5759,7 +5759,11 @@ pub fn translateC(...@@ -5759,7 +5759,11 @@ pub fn translateC(
57595759
5760 const gpa = comp.gpa;5760 const gpa = comp.gpa;
5761 const io = comp.io;5761 const io = comp.io;
5762 const tmp_basename = std.fmt.hex(std.crypto.random.int(u64));5762 const tmp_basename = r: {
5763 var x: u64 = undefined;
5764 io.random(@ptrCast(&x));
5765 break :r std.fmt.hex(x);
5766 };
5763 const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ tmp_basename;5767 const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ tmp_basename;
5764 const cache_dir = comp.dirs.local_cache.handle;5768 const cache_dir = comp.dirs.local_cache.handle;
5765 var cache_tmp_dir = try cache_dir.createDirPathOpen(io, tmp_sub_path, .{});5769 var cache_tmp_dir = try cache_dir.createDirPathOpen(io, tmp_sub_path, .{});
...@@ -6889,8 +6893,13 @@ fn spawnZigRc(...@@ -6889,8 +6893,13 @@ fn spawnZigRc(
6889}6893}
68906894
6891pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {6895pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
6896 const io = comp.io;
6897 const rand_int = r: {
6898 var x: u64 = undefined;
6899 io.random(@ptrCast(&x));
6900 break :r x;
6901 };
6892 const s = fs.path.sep_str;6902 const s = fs.path.sep_str;
6893 const rand_int = std.crypto.random.int(u64);
6894 if (comp.dirs.local_cache.path) |p| {6903 if (comp.dirs.local_cache.path) |p| {
6895 return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });6904 return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
6896 } else {6905 } else {
src/Package.zig+2-2
...@@ -14,9 +14,9 @@ pub const Fingerprint = packed struct(u64) {...@@ -14,9 +14,9 @@ pub const Fingerprint = packed struct(u64) {
14 id: u32,14 id: u32,
15 checksum: u32,15 checksum: u32,
1616
17 pub fn generate(name: []const u8) Fingerprint {17 pub fn generate(rng: std.Random, name: []const u8) Fingerprint {
18 return .{18 return .{
19 .id = std.crypto.random.intRangeLessThan(u32, 1, 0xffffffff),19 .id = rng.intRangeLessThan(u32, 1, 0xffffffff),
20 .checksum = std.hash.Crc32.hash(name),20 .checksum = std.hash.Crc32.hash(name),
21 };21 };
22 }22 }
src/Package/Fetch.zig+14-4
...@@ -494,7 +494,11 @@ fn runResource(...@@ -494,7 +494,11 @@ fn runResource(
494 const eb = &f.error_bundle;494 const eb = &f.error_bundle;
495 const s = fs.path.sep_str;495 const s = fs.path.sep_str;
496 const cache_root = f.job_queue.global_cache;496 const cache_root = f.job_queue.global_cache;
497 const rand_int = std.crypto.random.int(u64);497 const rand_int = r: {
498 var x: u64 = undefined;
499 io.random(@ptrCast(&x));
500 break :r x;
501 };
498 const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(rand_int);502 const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(rand_int);
499503
500 const package_sub_path = blk: {504 const package_sub_path = blk: {
...@@ -690,7 +694,9 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void {...@@ -690,7 +694,9 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void {
690 return error.FetchFailed;694 return error.FetchFailed;
691 }695 }
692696
693 f.manifest = try Manifest.parse(arena, ast.*, .{697 const rng: std.Random.IoSource = .{ .io = io };
698
699 f.manifest = try Manifest.parse(arena, ast.*, rng.interface(), .{
694 .allow_missing_paths_field = f.allow_missing_paths_field,700 .allow_missing_paths_field = f.allow_missing_paths_field,
695 .allow_missing_fingerprint = f.allow_missing_fingerprint,701 .allow_missing_fingerprint = f.allow_missing_fingerprint,
696 .allow_name_string = f.allow_name_string,702 .allow_name_string = f.allow_name_string,
...@@ -1305,7 +1311,11 @@ fn unzip(...@@ -1305,7 +1311,11 @@ fn unzip(
1305 zip_path[prefix.len + random_len ..].* = suffix.*;1311 zip_path[prefix.len + random_len ..].* = suffix.*;
13061312
1307 var zip_file = while (true) {1313 var zip_file = while (true) {
1308 const random_integer = std.crypto.random.int(u64);1314 const random_integer = r: {
1315 var x: u64 = undefined;
1316 io.random(@ptrCast(&x));
1317 break :r x;
1318 };
1309 zip_path[prefix.len..][0..random_len].* = std.fmt.hex(random_integer);1319 zip_path[prefix.len..][0..random_len].* = std.fmt.hex(random_integer);
13101320
1311 break cache_root.handle.createFile(io, &zip_path, .{1321 break cache_root.handle.createFile(io, &zip_path, .{
...@@ -1466,7 +1476,7 @@ pub fn renameTmpIntoCache(io: Io, cache_dir: Io.Dir, tmp_dir_sub_path: []const u...@@ -1466,7 +1476,7 @@ pub fn renameTmpIntoCache(io: Io, cache_dir: Io.Dir, tmp_dir_sub_path: []const u
1466 };1476 };
1467 continue;1477 continue;
1468 },1478 },
1469 error.PathAlreadyExists, error.AccessDenied => {1479 error.DirNotEmpty, error.AccessDenied => {
1470 // Package has been already downloaded and may already be in use on the system.1480 // Package has been already downloaded and may already be in use on the system.
1471 cache_dir.deleteTree(io, tmp_dir_sub_path) catch {1481 cache_dir.deleteTree(io, tmp_dir_sub_path) catch {
1472 // Garbage files leftover in zig-cache/tmp/ is, as they say1482 // Garbage files leftover in zig-cache/tmp/ is, as they say
src/Package/Manifest.zig+14-8
...@@ -57,7 +57,7 @@ pub const ParseOptions = struct {...@@ -57,7 +57,7 @@ pub const ParseOptions = struct {
5757
58pub const Error = Allocator.Error;58pub const Error = Allocator.Error;
5959
60pub fn parse(gpa: Allocator, ast: Ast, options: ParseOptions) Error!Manifest {60pub fn parse(gpa: Allocator, ast: Ast, rng: std.Random, options: ParseOptions) Error!Manifest {
61 const main_node_index = ast.nodeData(.root).node;61 const main_node_index = ast.nodeData(.root).node;
6262
63 var arena_instance = std.heap.ArenaAllocator.init(gpa);63 var arena_instance = std.heap.ArenaAllocator.init(gpa);
...@@ -87,7 +87,7 @@ pub fn parse(gpa: Allocator, ast: Ast, options: ParseOptions) Error!Manifest {...@@ -87,7 +87,7 @@ pub fn parse(gpa: Allocator, ast: Ast, options: ParseOptions) Error!Manifest {
87 defer p.dependencies.deinit(gpa);87 defer p.dependencies.deinit(gpa);
88 defer p.paths.deinit(gpa);88 defer p.paths.deinit(gpa);
8989
90 p.parseRoot(main_node_index) catch |err| switch (err) {90 p.parseRoot(main_node_index, rng) catch |err| switch (err) {
91 error.ParseFailure => assert(p.errors.items.len > 0),91 error.ParseFailure => assert(p.errors.items.len > 0),
92 else => |e| return e,92 else => |e| return e,
93 };93 };
...@@ -157,7 +157,7 @@ const Parse = struct {...@@ -157,7 +157,7 @@ const Parse = struct {
157157
158 const InnerError = error{ ParseFailure, OutOfMemory };158 const InnerError = error{ ParseFailure, OutOfMemory };
159159
160 fn parseRoot(p: *Parse, node: Ast.Node.Index) !void {160 fn parseRoot(p: *Parse, node: Ast.Node.Index, rng: std.Random) !void {
161 const ast = p.ast;161 const ast = p.ast;
162 const main_token = ast.nodeMainToken(node);162 const main_token = ast.nodeMainToken(node);
163163
...@@ -217,13 +217,13 @@ const Parse = struct {...@@ -217,13 +217,13 @@ const Parse = struct {
217 if (fingerprint) |n| {217 if (fingerprint) |n| {
218 if (!n.validate(p.name)) {218 if (!n.validate(p.name)) {
219 return fail(p, main_token, "invalid fingerprint: 0x{x}; if this is a new or forked package, use this value: 0x{x}", .{219 return fail(p, main_token, "invalid fingerprint: 0x{x}; if this is a new or forked package, use this value: 0x{x}", .{
220 n.int(), Package.Fingerprint.generate(p.name).int(),220 n.int(), Package.Fingerprint.generate(rng, p.name).int(),
221 });221 });
222 }222 }
223 p.id = n.id;223 p.id = n.id;
224 } else if (!p.allow_missing_fingerprint) {224 } else if (!p.allow_missing_fingerprint) {
225 try appendError(p, main_token, "missing top-level 'fingerprint' field; suggested value: 0x{x}", .{225 try appendError(p, main_token, "missing top-level 'fingerprint' field; suggested value: 0x{x}", .{
226 Package.Fingerprint.generate(p.name).int(),226 Package.Fingerprint.generate(rng, p.name).int(),
227 });227 });
228 } else {228 } else {
229 p.id = 0;229 p.id = 0;
...@@ -623,7 +623,9 @@ test "basic" {...@@ -623,7 +623,9 @@ test "basic" {
623623
624 try testing.expect(ast.errors.len == 0);624 try testing.expect(ast.errors.len == 0);
625625
626 var manifest = try Manifest.parse(gpa, ast, .{});626 var rng = std.Random.DefaultPrng.init(0);
627
628 var manifest = try Manifest.parse(gpa, ast, rng.random(), .{});
627 defer manifest.deinit(gpa);629 defer manifest.deinit(gpa);
628630
629 try testing.expect(manifest.errors.len == 0);631 try testing.expect(manifest.errors.len == 0);
...@@ -666,7 +668,9 @@ test "minimum_zig_version" {...@@ -666,7 +668,9 @@ test "minimum_zig_version" {
666668
667 try testing.expect(ast.errors.len == 0);669 try testing.expect(ast.errors.len == 0);
668670
669 var manifest = try Manifest.parse(gpa, ast, .{});671 var rng = std.Random.DefaultPrng.init(0);
672
673 var manifest = try Manifest.parse(gpa, ast, rng.random(), .{});
670 defer manifest.deinit(gpa);674 defer manifest.deinit(gpa);
671675
672 try testing.expect(manifest.errors.len == 0);676 try testing.expect(manifest.errors.len == 0);
...@@ -698,7 +702,9 @@ test "minimum_zig_version - invalid version" {...@@ -698,7 +702,9 @@ test "minimum_zig_version - invalid version" {
698702
699 try testing.expect(ast.errors.len == 0);703 try testing.expect(ast.errors.len == 0);
700704
701 var manifest = try Manifest.parse(gpa, ast, .{});705 var rng = std.Random.DefaultPrng.init(0);
706
707 var manifest = try Manifest.parse(gpa, ast, rng.random(), .{});
702 defer manifest.deinit(gpa);708 defer manifest.deinit(gpa);
703709
704 try testing.expect(manifest.errors.len == 1);710 try testing.expect(manifest.errors.len == 1);
src/link.zig+6-1
...@@ -616,8 +616,13 @@ pub const File = struct {...@@ -616,8 +616,13 @@ pub const File = struct {
616 // it will return ETXTBSY. So instead, we copy the file, atomically rename it616 // it will return ETXTBSY. So instead, we copy the file, atomically rename it
617 // over top of the exe path, and then proceed normally. This changes the inode,617 // over top of the exe path, and then proceed normally. This changes the inode,
618 // avoiding the error.618 // avoiding the error.
619 const random_integer = r: {
620 var x: u32 = undefined;
621 io.random(@ptrCast(&x));
622 break :r x;
623 };
619 const tmp_sub_path = try std.fmt.allocPrint(gpa, "{s}-{x}", .{624 const tmp_sub_path = try std.fmt.allocPrint(gpa, "{s}-{x}", .{
620 emit.sub_path, std.crypto.random.int(u32),625 emit.sub_path, random_integer,
621 });626 });
622 defer gpa.free(tmp_sub_path);627 defer gpa.free(tmp_sub_path);
623 try emit.root_dir.handle.copyFile(emit.sub_path, emit.root_dir.handle, tmp_sub_path, io, .{});628 try emit.root_dir.handle.copyFile(emit.sub_path, emit.root_dir.handle, tmp_sub_path, io, .{});
src/link/Lld.zig+5-1
...@@ -1636,7 +1636,11 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi...@@ -1636,7 +1636,11 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi
1636 const err = switch (first_err) {1636 const err = switch (first_err) {
1637 error.NameTooLong => err: {1637 error.NameTooLong => err: {
1638 const s = fs.path.sep_str;1638 const s = fs.path.sep_str;
1639 const rand_int = std.crypto.random.int(u64);1639 const rand_int = r: {
1640 var x: u64 = undefined;
1641 io.random(@ptrCast(&x));
1642 break :r x;
1643 };
1640 const rsp_path = "tmp" ++ s ++ std.fmt.hex(rand_int) ++ ".rsp";1644 const rsp_path = "tmp" ++ s ++ std.fmt.hex(rand_int) ++ ".rsp";
16411645
1642 const rsp_file = try comp.dirs.local_cache.handle.createFile(io, rsp_path, .{});1646 const rsp_file = try comp.dirs.local_cache.handle.createFile(io, rsp_path, .{});
src/main.zig+19-12
...@@ -3395,7 +3395,7 @@ fn buildOutputType(...@@ -3395,7 +3395,7 @@ fn buildOutputType(
3395 // "-" is stdin. Dump it to a real file.3395 // "-" is stdin. Dump it to a real file.
3396 const sep = fs.path.sep_str;3396 const sep = fs.path.sep_str;
3397 const dump_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-dump-stdin{s}", .{3397 const dump_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-dump-stdin{s}", .{
3398 std.crypto.random.int(u64), ext.canonicalName(target),3398 randInt(io, u64), ext.canonicalName(target),
3399 });3399 });
3400 try dirs.local_cache.handle.createDirPath(io, "tmp");3400 try dirs.local_cache.handle.createDirPath(io, "tmp");
34013401
...@@ -4433,7 +4433,7 @@ fn runOrTest(...@@ -4433,7 +4433,7 @@ fn runOrTest(
4433 try argv.append(exe_path);4433 try argv.append(exe_path);
4434 if (arg_mode == .zig_test) {4434 if (arg_mode == .zig_test) {
4435 try argv.append(4435 try argv.append(
4436 try std.fmt.allocPrint(arena, "--seed=0x{x}", .{std.crypto.random.int(u32)}),4436 try std.fmt.allocPrint(arena, "--seed=0x{x}", .{randInt(io, u32)}),
4437 );4437 );
4438 }4438 }
4439 } else {4439 } else {
...@@ -4763,7 +4763,8 @@ fn cmdInit(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !...@@ -4763,7 +4763,8 @@ fn cmdInit(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !
4763 const cwd_basename = fs.path.basename(cwd_path);4763 const cwd_basename = fs.path.basename(cwd_path);
4764 const sanitized_root_name = try sanitizeExampleName(arena, cwd_basename);4764 const sanitized_root_name = try sanitizeExampleName(arena, cwd_basename);
47654765
4766 const fingerprint: Package.Fingerprint = .generate(sanitized_root_name);4766 const rng: std.Random.IoSource = .{ .io = io };
4767 const fingerprint: Package.Fingerprint = .generate(rng.interface(), sanitized_root_name);
47674768
4768 switch (template) {4769 switch (template) {
4769 .example => {4770 .example => {
...@@ -4919,7 +4920,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -4919,7 +4920,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
49194920
4920 try child_argv.appendSlice(&.{4921 try child_argv.appendSlice(&.{
4921 "--seed",4922 "--seed",
4922 try std.fmt.allocPrint(arena, "0x{x}", .{std.crypto.random.int(u32)}),4923 try std.fmt.allocPrint(arena, "0x{x}", .{randInt(io, u32)}),
4923 });4924 });
4924 const argv_index_seed = child_argv.items.len - 1;4925 const argv_index_seed = child_argv.items.len - 1;
49254926
...@@ -4937,7 +4938,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -4937,7 +4938,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
4937 // the strategy is to choose a temporary file name ahead of time, and then4938 // the strategy is to choose a temporary file name ahead of time, and then
4938 // read this file in the parent to obtain the results, in the case the child4939 // read this file in the parent to obtain the results, in the case the child
4939 // exits with code 3.4940 // exits with code 3.
4940 const results_tmp_file_nonce = std.fmt.hex(std.crypto.random.int(u64));4941 const results_tmp_file_nonce = std.fmt.hex(randInt(io, u64));
4941 try child_argv.append("-Z" ++ results_tmp_file_nonce);4942 try child_argv.append("-Z" ++ results_tmp_file_nonce);
49424943
4943 var color: Color = .auto;4944 var color: Color = .auto;
...@@ -7223,7 +7224,7 @@ fn createDependenciesModule(...@@ -7223,7 +7224,7 @@ fn createDependenciesModule(
7223) !*Package.Module {7224) !*Package.Module {
7224 // Atomically create the file in a directory named after the hash of its contents.7225 // Atomically create the file in a directory named after the hash of its contents.
7225 const basename = "dependencies.zig";7226 const basename = "dependencies.zig";
7226 const rand_int = std.crypto.random.int(u64);7227 const rand_int = randInt(io, u64);
7227 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);7228 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
7228 {7229 {
7229 var tmp_dir = try dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{});7230 var tmp_dir = try dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{});
...@@ -7339,6 +7340,8 @@ fn loadManifest(...@@ -7339,6 +7340,8 @@ fn loadManifest(
7339 io: Io,7340 io: Io,
7340 options: LoadManifestOptions,7341 options: LoadManifestOptions,
7341) !struct { Package.Manifest, Ast } {7342) !struct { Package.Manifest, Ast } {
7343 const rng: std.Random.IoSource = .{ .io = io };
7344
7342 const manifest_bytes = while (true) {7345 const manifest_bytes = while (true) {
7343 break options.dir.readFileAllocOptions(7346 break options.dir.readFileAllocOptions(
7344 io,7347 io,
...@@ -7360,15 +7363,13 @@ fn loadManifest(...@@ -7360,15 +7363,13 @@ fn loadManifest(
7360 , .{7363 , .{
7361 options.root_name,7364 options.root_name,
7362 build_options.version,7365 build_options.version,
7363 Package.Fingerprint.generate(options.root_name).int(),7366 Package.Fingerprint.generate(rng.interface(), options.root_name).int(),
7364 }) catch |e| {7367 }) catch |e| {
7365 fatal("unable to write {s}: {s}", .{ Package.Manifest.basename, @errorName(e) });7368 fatal("unable to write {s}: {t}", .{ Package.Manifest.basename, e });
7366 };7369 };
7367 continue;7370 continue;
7368 },7371 },
7369 else => |e| fatal("unable to load {s}: {s}", .{7372 else => |e| fatal("unable to load {s}: {t}", .{ Package.Manifest.basename, e }),
7370 Package.Manifest.basename, @errorName(e),
7371 }),
7372 };7373 };
7373 };7374 };
7374 var ast = try Ast.parse(gpa, manifest_bytes, .zon);7375 var ast = try Ast.parse(gpa, manifest_bytes, .zon);
...@@ -7379,7 +7380,7 @@ fn loadManifest(...@@ -7379,7 +7380,7 @@ fn loadManifest(
7379 process.exit(2);7380 process.exit(2);
7380 }7381 }
73817382
7382 var manifest = try Package.Manifest.parse(gpa, ast, .{});7383 var manifest = try Package.Manifest.parse(gpa, ast, rng.interface(), .{});
7383 errdefer manifest.deinit(gpa);7384 errdefer manifest.deinit(gpa);
73847385
7385 if (manifest.errors.len > 0) {7386 if (manifest.errors.len > 0) {
...@@ -7632,3 +7633,9 @@ fn setThreadLimit(n: usize) void {...@@ -7632,3 +7633,9 @@ fn setThreadLimit(n: usize) void {
7632 threaded_impl_ptr.setAsyncLimit(limit);7633 threaded_impl_ptr.setAsyncLimit(limit);
7633 threaded_impl_ptr.concurrent_limit = limit;7634 threaded_impl_ptr.concurrent_limit = limit;
7634}7635}
7636
7637fn randInt(io: Io, comptime T: type) T {
7638 var x: T = undefined;
7639 io.random(@ptrCast(&x));
7640 return x;
7641}
test/standalone/simple/guess_number/main.zig+2-1
...@@ -10,7 +10,8 @@ pub fn main(init: std.process.Init) !void {...@@ -10,7 +10,8 @@ pub fn main(init: std.process.Init) !void {
1010
11 try out.writeAll("Welcome to the Guess Number Game in Zig.\n");11 try out.writeAll("Welcome to the Guess Number Game in Zig.\n");
1212
13 const answer = std.crypto.random.intRangeLessThan(u8, 0, 100) + 1;13 var rng: std.Random.IoSource = .{ .io = init.io };
14 const answer = rng.interface().intRangeLessThan(u8, 0, 100) + 1;
1415
15 while (true) {16 while (true) {
16 try out.writeAll("\nGuess a number between 1 and 100: ");17 try out.writeAll("\nGuess a number between 1 and 100: ");
test/standalone/windows_argv/build.zig+1-1
...@@ -52,7 +52,7 @@ pub fn build(b: *std.Build) !void {...@@ -52,7 +52,7 @@ pub fn build(b: *std.Build) !void {
5252
53 const fuzz_seed = b.option(u64, "seed", "Seed to use for the PRNG (default: random)") orelse seed: {53 const fuzz_seed = b.option(u64, "seed", "Seed to use for the PRNG (default: random)") orelse seed: {
54 var buf: [8]u8 = undefined;54 var buf: [8]u8 = undefined;
55 try std.posix.getrandom(&buf);55 b.graph.io.random(&buf);
56 break :seed std.mem.readInt(u64, &buf, builtin.cpu.arch.endian());56 break :seed std.mem.readInt(u64, &buf, builtin.cpu.arch.endian());
57 };57 };
58 const fuzz_seed_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_seed}) catch @panic("oom");58 const fuzz_seed_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_seed}) catch @panic("oom");
test/standalone/windows_argv/fuzz.zig+2-1
...@@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator;...@@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator;
55
6pub fn main(init: std.process.Init) !void {6pub fn main(init: std.process.Init) !void {
7 const gpa = init.gpa;7 const gpa = init.gpa;
8 const io = init.io;
8 const args = try init.minimal.args.toSlice(init.arena.allocator());9 const args = try init.minimal.args.toSlice(init.arena.allocator());
910
10 if (args.len < 2) return error.MissingArgs;11 if (args.len < 2) return error.MissingArgs;
...@@ -23,7 +24,7 @@ pub fn main(init: std.process.Init) !void {...@@ -23,7 +24,7 @@ pub fn main(init: std.process.Init) !void {
23 if (args.len < 4) {24 if (args.len < 4) {
24 rand_seed = true;25 rand_seed = true;
25 var buf: [8]u8 = undefined;26 var buf: [8]u8 = undefined;
26 try std.posix.getrandom(&buf);27 io.random(&buf);
27 break :seed std.mem.readInt(u64, &buf, builtin.cpu.arch.endian());28 break :seed std.mem.readInt(u64, &buf, builtin.cpu.arch.endian());
28 }29 }
29 break :seed try std.fmt.parseUnsigned(u64, args[3], 10);30 break :seed try std.fmt.parseUnsigned(u64, args[3], 10);
test/standalone/windows_bat_args/build.zig+1-1
...@@ -65,7 +65,7 @@ pub fn build(b: *std.Build) !void {...@@ -65,7 +65,7 @@ pub fn build(b: *std.Build) !void {
6565
66 const fuzz_seed = b.option(u64, "seed", "Seed to use for the PRNG (default: random)") orelse seed: {66 const fuzz_seed = b.option(u64, "seed", "Seed to use for the PRNG (default: random)") orelse seed: {
67 var buf: [8]u8 = undefined;67 var buf: [8]u8 = undefined;
68 try std.posix.getrandom(&buf);68 b.graph.io.random(&buf);
69 break :seed std.mem.readInt(u64, &buf, builtin.cpu.arch.endian());69 break :seed std.mem.readInt(u64, &buf, builtin.cpu.arch.endian());
70 };70 };
71 const fuzz_seed_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_seed}) catch @panic("oom");71 const fuzz_seed_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_seed}) catch @panic("oom");
test/standalone/windows_bat_args/fuzz.zig+1-1
...@@ -22,7 +22,7 @@ pub fn main(init: std.process.Init) !void {...@@ -22,7 +22,7 @@ pub fn main(init: std.process.Init) !void {
22 const seed_arg = it.next() orelse {22 const seed_arg = it.next() orelse {
23 rand_seed = true;23 rand_seed = true;
24 var buf: [8]u8 = undefined;24 var buf: [8]u8 = undefined;
25 try std.posix.getrandom(&buf);25 io.random(&buf);
26 break :seed std.mem.readInt(u64, &buf, builtin.cpu.arch.endian());26 break :seed std.mem.readInt(u64, &buf, builtin.cpu.arch.endian());
27 };27 };
28 break :seed try std.fmt.parseUnsigned(u64, seed_arg, 10);28 break :seed try std.fmt.parseUnsigned(u64, seed_arg, 10);
tools/doctest.zig+4-3
...@@ -78,9 +78,10 @@ pub fn main(init: std.process.Init) !void {...@@ -78,9 +78,10 @@ pub fn main(init: std.process.Init) !void {
78 const code = try parseManifest(arena, source_bytes);78 const code = try parseManifest(arena, source_bytes);
79 const source = stripManifest(source_bytes);79 const source = stripManifest(source_bytes);
8080
81 const tmp_dir_path = try std.fmt.allocPrint(arena, "{s}/tmp/{x}", .{81 var random_integer: u64 = undefined;
82 cache_root, std.crypto.random.int(u64),82 io.random(@ptrCast(&random_integer));
83 });83
84 const tmp_dir_path = try std.fmt.allocPrint(arena, "{s}/tmp/{x}", .{ cache_root, random_integer });
84 Dir.cwd().createDirPath(io, tmp_dir_path) catch |err|85 Dir.cwd().createDirPath(io, tmp_dir_path) catch |err|
85 fatal("unable to create tmp dir '{s}': {t}", .{ tmp_dir_path, err });86 fatal("unable to create tmp dir '{s}': {t}", .{ tmp_dir_path, err });
86 defer Dir.cwd().deleteTree(io, tmp_dir_path) catch |err| std.log.err("unable to delete '{s}': {t}", .{87 defer Dir.cwd().deleteTree(io, tmp_dir_path) catch |err| std.log.err("unable to delete '{s}': {t}", .{
tools/incr-check.zig+9-4
...@@ -100,7 +100,7 @@ pub fn main(init: std.process.Init) !void {...@@ -100,7 +100,7 @@ pub fn main(init: std.process.Init) !void {
100 const prog_node = std.Progress.start(io, .{});100 const prog_node = std.Progress.start(io, .{});
101 defer prog_node.end();101 defer prog_node.end();
102102
103 const rand_int = std.crypto.random.int(u64);103 const rand_int = rand64(io);
104 const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int);104 const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int);
105 var tmp_dir = try Dir.cwd().createDirPathOpen(io, tmp_dir_path, .{});105 var tmp_dir = try Dir.cwd().createDirPathOpen(io, tmp_dir_path, .{});
106 defer {106 defer {
...@@ -452,20 +452,19 @@ const Eval = struct {...@@ -452,20 +452,19 @@ const Eval = struct {
452 std.debug.assert(eval.target.backend == .sema);452 std.debug.assert(eval.target.backend == .sema);
453 return;453 return;
454 };454 };
455 const io = eval.io;
455456
456 const binary_path = switch (eval.target.backend) {457 const binary_path = switch (eval.target.backend) {
457 .sema => unreachable,458 .sema => unreachable,
458 .selfhosted, .llvm => emitted_path,459 .selfhosted, .llvm => emitted_path,
459 .cbe => bin: {460 .cbe => bin: {
460 const rand_int = std.crypto.random.int(u64);461 const rand_int = rand64(io);
461 const out_bin_name = "./out_" ++ std.fmt.hex(rand_int);462 const out_bin_name = "./out_" ++ std.fmt.hex(rand_int);
462 try eval.buildCOutput(emitted_path, out_bin_name, prog_node);463 try eval.buildCOutput(emitted_path, out_bin_name, prog_node);
463 break :bin out_bin_name;464 break :bin out_bin_name;
464 },465 },
465 };466 };
466467
467 const io = eval.io;
468
469 var argv_buf: [2][]const u8 = undefined;468 var argv_buf: [2][]const u8 = undefined;
470 const argv: []const []const u8, const is_foreign: bool = sw: switch (std.zig.system.getExternalExecutor(469 const argv: []const []const u8, const is_foreign: bool = sw: switch (std.zig.system.getExternalExecutor(
471 io,470 io,
...@@ -957,3 +956,9 @@ fn parseExpectedError(str: []const u8, l: usize) Case.ExpectedError {...@@ -957,3 +956,9 @@ fn parseExpectedError(str: []const u8, l: usize) Case.ExpectedError {
957 .msg = message,956 .msg = message,
958 };957 };
959}958}
959
960fn rand64(io: Io) u64 {
961 var x: u64 = undefined;
962 io.random(@ptrCast(&x));
963 return x;
964}