| author | |
| committer | |
| log | 62d6bbc7dc36dbb2f45da08e767076157deeb259 |
| tree | 6f9e22b97a6cfee83aecf3fcc3bc5cd5f2a76806 |
| parent | 867501d9d2f757f99af59b1904b251aa63262e23 |
| parent | 006afece53af66401b941e25eba03efab6e9251f |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/3070966 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 | } |
| 1218 | 1218 | ||
| 1219 | fn getRandomFilename(d: *Driver, buf: *[std.fs.max_name_bytes]u8, extension: []const u8) ![]const u8 { | 1219 | fn 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); |
| 1222 | 1223 | ||
| 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); |
| 1227 | 1228 |
lib/compiler/build_runner.zig-1| ... | @@ -21,7 +21,6 @@ pub const dependencies = @import("@dependencies"); | ... | @@ -21,7 +21,6 @@ pub const dependencies = @import("@dependencies"); |
| 21 | pub const std_options: std.Options = .{ | 21 | pub 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 | }; |
| 26 | 25 | ||
| 27 | pub fn main(init: process.Init.Minimal) !void { | 26 | pub 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 { |
| 111 | pub const MakeOptions = struct { | 111 | pub 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, |
| 482 | 482 | }) 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 | }; | ||
| 494 | 486 | ||
| 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 | }; |
| 500 | 492 | ||
| 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 | }; |
| 985 | 985 | ||
| 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); |
| 989 | 990 | ||
| 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 | } |
| 1129 | 1130 | ||
| 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; |
| 295 | 295 | ||
| 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); |
| 298 | 299 | ||
| 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, |
| 733 | 734 | ||
| 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 |
| 2242 | pub fn unlockStderr(io: Io) void { | 2246 | pub 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`. | ||
| 2262 | pub fn random(io: Io, buffer: []u8) void { | ||
| 2263 | return io.vtable.random(io.userdata, buffer); | ||
| 2264 | } | ||
| 2265 | |||
| 2266 | pub 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`. | ||
| 2279 | pub 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 |
| 936 | pub const RenameError = error{ | 936 | pub const RenameError = error{ |
| 937 | /// In WASI, this error may occur when the file descriptor does | 937 | /// 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 slower | 962 | /// 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; |
| 968 | 967 | ||
| 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 existing | 972 | /// 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. |
| 979 | pub fn rename( | 978 | pub 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 | } |
| 995 | 994 | ||
| 995 | pub 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. | ||
| 1018 | pub 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 | |||
| 996 | pub const HardLinkOptions = File.HardLinkOptions; | 1028 | pub const HardLinkOptions = File.HardLinkOptions; |
| 997 | 1029 | ||
| 998 | pub const HardLinkError = File.HardLinkError; | 1030 | pub const HardLinkError = File.HardLinkError; |
| ... | @@ -1098,8 +1130,10 @@ pub fn symLinkAtomic( | ... | @@ -1098,8 +1130,10 @@ pub fn symLinkAtomic( |
| 1098 | 1130 | ||
| 1099 | const temp_path = temp_path_buf[0..temp_path_len]; | 1131 | const temp_path = temp_path_buf[0..temp_path_len]; |
| 1100 | 1132 | ||
| 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); |
| 1104 | 1138 | ||
| 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 | } |
| 710 | 710 | ||
| 711 | pub const HardLinkOptions = struct { | 711 | pub const HardLinkOptions = struct { |
| 712 | follow_symlinks: bool = true, | 712 | follow_symlinks: bool = false, |
| 713 | }; | 713 | }; |
| 714 | 714 | ||
| 715 | pub const HardLinkError = error{ | 715 | pub 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; |
| 732 | 732 |
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 | } |
| 39 | 39 | ||
| 40 | pub const LinkError = Dir.HardLinkError; | 40 | pub const LinkError = File.HardLinkError || Dir.RenamePreserveError; |
| 41 | 41 | ||
| 42 | /// Atomically materializes the file into place, failing with | 42 | /// 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`. | ||
| 44 | pub fn link(af: *Atomic, io: Io) LinkError!void { | 48 | pub 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, |
| 65 | environ: Environ, | 65 | environ: Environ, |
| 66 | 66 | ||
| 67 | null_file: NullFile = .{}, | 67 | null_file: NullFile = .{}, |
| 68 | random_file: RandomFile = .{}, | ||
| 69 | |||
| 70 | csprng: Csprng = .{}, | ||
| 71 | |||
| 72 | pub 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 | }; | ||
| 68 | 84 | ||
| 69 | pub const Argv0 = switch (native_os) { | 85 | pub 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 | }; |
| 153 | 169 | ||
| 170 | pub 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 | |||
| 154 | pub const Pid = if (native_os == .linux) enum(posix.pid_t) { | 179 | pub 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, |
| 587 | 612 | ||
| 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 | } |
| 1290 | 1318 | ||
| ... | @@ -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; |
| 1318 | 1347 | ||
| ... | @@ -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, |
| 1468 | 1498 | ||
| 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, |
| 1616 | 1650 | ||
| 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 | }); |
| 1705 | const linux_copy_file_range_sys = if (linux_copy_file_range_use_c) std.c else std.os.linux; | 1742 | const linux_copy_file_range_sys = if (linux_copy_file_range_use_c) std.c else std.os.linux; |
| 1706 | 1743 | ||
| 1744 | const statx_use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) | ||
| 1745 | .{ .major = 30, .minor = 0, .patch = 0 } | ||
| 1746 | else | ||
| 1747 | .{ .major = 2, .minor = 28, .patch = 0 }); | ||
| 1748 | |||
| 1749 | const 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 | |||
| 1759 | const use_dev_urandom = @TypeOf(posix.system.getrandom) == void and native_os == .linux; | ||
| 1760 | |||
| 1707 | fn async( | 1761 | fn 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 directory | 2399 | // It is important to return an error if it's not a directory |
| 2346 | // this is important because otherwise a dangling symlink | 2400 | // because otherwise a dangling symlink could cause an infinite |
| 2347 | // could cause an infinite loop | 2401 | // 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; | ||
| 2546 | 2596 | ||
| 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 | } |
| 2693 | 2743 | ||
| 2744 | fn 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 | |||
| 2694 | fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 { | 2773 | fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 { |
| 2695 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 2774 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 2696 | 2775 | ||
| ... | @@ -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; | ||
| 2786 | 2861 | ||
| 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 or | 3526 | // 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 | }; |
| 3638 | 3714 | ||
| 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; |
| 4070 | 4148 | ||
| 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); |
| 5171 | 5251 | ||
| 5172 | const flags: u32 = if (!options.follow_symlinks) | 5252 | const flags: u32 = if (options.follow_symlinks) |
| 5173 | posix.AT.SYMLINK_NOFOLLOW | posix.AT.EMPTY_PATH | 5253 | posix.AT.SYMLINK_FOLLOW | posix.AT.EMPTY_PATH |
| 5174 | else | 5254 | else |
| 5175 | posix.AT.EMPTY_PATH; | 5255 | posix.AT.EMPTY_PATH; |
| 5176 | 5256 | ||
| 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 | } |
| 5179 | 5268 | ||
| 5180 | fn linkat( | 5269 | fn 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 | } | ||
| 5620 | 5714 | ||
| 5715 | fn 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 | |||
| 5732 | fn 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; | ||
| 5626 | 5744 | ||
| 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); |
| 5801 | 5919 | ||
| 5920 | return renameat(old_dir.handle, old_sub_path_posix, new_dir.handle, new_sub_path_posix); | ||
| 5921 | } | ||
| 5922 | |||
| 5923 | fn 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 | |||
| 5973 | fn 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 | |||
| 6010 | fn 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; | ||
| 6326 | 6535 | ||
| 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); |
| 6370 | 6579 | ||
| 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); |
| 7630 | 7840 | ||
| 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 | } |
| 7634 | 7844 | ||
| ... | @@ -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 | } |
| 12288 | 12489 | ||
| 12490 | fn 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 | |||
| 12289 | fn statFromPosix(st: *const posix.Stat) File.Stat { | 12503 | fn 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( |
| 12441 | 12655 | ||
| 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 | } |
| 13855 | 14070 | ||
| 14071 | fn 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 | |||
| 13856 | fn getNulHandle(t: *Threaded) !windows.HANDLE { | 14127 | fn 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 | } |
| 14937 | 15208 | ||
| 15209 | fn 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 | |||
| 15221 | fn 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 | |||
| 15255 | fn 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 | |||
| 15272 | fn 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 | |||
| 15278 | fn 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 | |||
| 15285 | fn 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 | |||
| 15413 | fn 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 | |||
| 14938 | test { | 15508 | test { |
| 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" { |
| 275 | 275 | ||
| 276 | const io = testing.io; | 276 | const io = testing.io; |
| 277 | 277 | ||
| 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); |
| 280 | 280 | ||
| 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 | } |
| 310 | 310 | ||
| 311 | fn generateFileName(base_name: []const u8) ![]const u8 { | 311 | fn 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 up | 564 | 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 | |||
| 568 | test "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 | |||
| 582 | test "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 will | 2 | //! 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. |
| 4 | const Random = @This(); | ||
| 6 | 5 | ||
| 7 | const std = @import("std.zig"); | 6 | const std = @import("std.zig"); |
| 8 | const math = std.math; | 7 | const math = std.math; |
| 9 | const mem = std.mem; | 8 | const mem = std.mem; |
| 10 | const assert = std.debug.assert; | 9 | const assert = std.debug.assert; |
| 11 | const maxInt = std.math.maxInt; | 10 | const maxInt = std.math.maxInt; |
| 12 | const Random = @This(); | ||
| 13 | 11 | ||
| 14 | /// Fast unbiased random numbers. | 12 | /// Fast unbiased random numbers. |
| 15 | pub const DefaultPrng = Xoshiro256; | 13 | pub 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"); |
| 35 | ptr: *anyopaque, | 33 | ptr: *anyopaque, |
| 36 | fillFn: *const fn (ptr: *anyopaque, buf: []u8) void, | 34 | fillFn: *const fn (ptr: *anyopaque, buf: []u8) void, |
| 37 | 35 | ||
| 36 | pub 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 | |||
| 38 | pub fn init(pointer: anytype, comptime fillFn: fn (ptr: @TypeOf(pointer), buf: []u8) void) Random { | 52 | pub 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 pointer | 54 | 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; |
| 20 | 20 | ||
| 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. |
| 22 | pub fn init(secret_seed: [secret_seed_length]u8) Self { | 22 | pub 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 | } |
| 437 | 437 | ||
| 438 | test "CSPRNG" { | 438 | test "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. |
| 236 | pub const ff = @import("crypto/ff.zig"); | 236 | pub const ff = @import("crypto/ff.zig"); |
| 237 | 237 | ||
| 238 | /// This is a thread-local, cryptographically secure pseudo random number generator. | ||
| 239 | pub const random = @import("crypto/tlcsprng.zig").interface; | ||
| 240 | |||
| 241 | /// Encoding and decoding | 238 | /// Encoding and decoding |
| 242 | pub const codecs = @import("crypto/codecs.zig"); | 239 | pub const codecs = @import("crypto/codecs.zig"); |
| 243 | 240 | ||
| ... | @@ -306,6 +303,9 @@ test { | ... | @@ -306,6 +303,9 @@ test { |
| 306 | _ = dh.X25519; | 303 | _ = dh.X25519; |
| 307 | 304 | ||
| 308 | _ = kem.kyber_d00; | 305 | _ = kem.kyber_d00; |
| 306 | _ = kem.hybrid; | ||
| 307 | _ = kem.kyber_d00; | ||
| 308 | _ = kem.ml_kem; | ||
| 309 | 309 | ||
| 310 | _ = ecc.Curve25519; | 310 | _ = ecc.Curve25519; |
| 311 | _ = ecc.Edwards25519; | 311 | _ = ecc.Edwards25519; |
| ... | @@ -343,6 +343,7 @@ test { | ... | @@ -343,6 +343,7 @@ test { |
| 343 | 343 | ||
| 344 | _ = sign.Ed25519; | 344 | _ = sign.Ed25519; |
| 345 | _ = sign.ecdsa; | 345 | _ = sign.ecdsa; |
| 346 | _ = sign.mldsa; | ||
| 346 | 347 | ||
| 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 | } |
| 373 | 373 | ||
| 374 | test "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 | |||
| 381 | test "issue #4532: no index out of bounds" { | 374 | test "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 | } |
| 334 | 334 | ||
| 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 { |
| 389 | 387 | ||
| 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 | }; |
| 421 | 422 | ||
| 422 | /// Verify several signatures in a single operation, much faster than verifying signatures one-by-one | 423 | /// 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 { |
| 454 | 455 | ||
| 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 | } |
| 460 | 461 | ||
| ... | @@ -587,12 +588,14 @@ test "signature" { | ... | @@ -587,12 +588,14 @@ test "signature" { |
| 587 | } | 588 | } |
| 588 | 589 | ||
| 589 | test "batch verification" { | 590 | test "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); |
| 611 | 614 | ||
| 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 | } |
| 616 | 619 | ||
| ... | @@ -718,14 +721,15 @@ test "test vectors" { | ... | @@ -718,14 +721,15 @@ test "test vectors" { |
| 718 | } | 721 | } |
| 719 | 722 | ||
| 720 | test "with blind keys" { | 723 | test "with blind keys" { |
| 724 | const io = std.testing.io; | ||
| 721 | const BlindKeyPair = Ed25519.key_blinding.BlindKeyPair; | 725 | const BlindKeyPair = Ed25519.key_blinding.BlindKeyPair; |
| 722 | 726 | ||
| 723 | // Create a standard Ed25519 key pair | 727 | // Create a standard Ed25519 key pair |
| 724 | const kp = Ed25519.KeyPair.generate(); | 728 | const kp = Ed25519.KeyPair.generate(io); |
| 725 | 729 | ||
| 726 | // Create a random blinding seed | 730 | // 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); |
| 729 | 733 | ||
| 730 | // Blind the key pair | 734 | // 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 | } |
| 742 | 746 | ||
| 743 | test "signatures with streaming" { | 747 | test "signatures with streaming" { |
| 744 | const kp = Ed25519.KeyPair.generate(); | 748 | const io = std.testing.io; |
| 749 | const kp = Ed25519.KeyPair.generate(io); | ||
| 745 | 750 | ||
| 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 | } |
| 758 | 765 | ||
| 759 | test "key pair from secret key" { | 766 | test "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 | } |
| 789 | 797 | ||
| 790 | test "regular signature verifies with both verify and verifyStrict" { | 798 | test "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 | } |
| 576 | 576 | ||
| 577 | test "point addition/subtraction" { | 577 | test "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 | } |
| 623 | 624 | ||
| 624 | test "subgroup check" { | 625 | test "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 | } |
| 102 | 102 | ||
| 103 | /// Return a random scalar < L | 103 | /// Return a random scalar < L |
| 104 | pub fn random() CompressedScalar { | 104 | pub fn random(io: std.Io) CompressedScalar { |
| 105 | return Scalar.random().toBytes(); | 105 | return Scalar.random(io).toBytes(); |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | /// A scalar in unpacked representation | 108 | /// A scalar in unpacked representation |
| ... | @@ -560,10 +560,10 @@ pub const Scalar = struct { | ... | @@ -560,10 +560,10 @@ pub const Scalar = struct { |
| 560 | } | 560 | } |
| 561 | 561 | ||
| 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 | } |
| 880 | 880 | ||
| 881 | test "random scalar" { | 881 | test "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 | } |
| 886 | 887 |
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 | } |
| 42 | 42 | ||
| 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; |
| 534 | 534 | ||
| 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); |
| 537 | 537 | ||
| 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; |
| 17 | const EncodingError = phc_format.Error; | 17 | const EncodingError = phc_format.Error; |
| 18 | const Error = pwhash.Error; | 18 | const Error = pwhash.Error; |
| 19 | 19 | ||
| 20 | const salt_length: usize = 16; | 20 | pub const salt_length: usize = 16; |
| 21 | const salt_str_length: usize = 22; | 21 | const salt_str_length: usize = 22; |
| 22 | const ct_str_length: usize = 31; | 22 | const ct_str_length: usize = 31; |
| 23 | const ct_length: usize = 24; | 23 | const ct_length: usize = 24; |
| ... | @@ -426,7 +426,7 @@ pub const Params = struct { | ... | @@ -426,7 +426,7 @@ pub const Params = struct { |
| 426 | 426 | ||
| 427 | fn bcryptWithTruncation( | 427 | fn 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); |
| 439 | 439 | ||
| 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); |
| 447 | 447 | ||
| ... | @@ -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. |
| 468 | pub fn bcrypt( | 468 | pub 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 | } |
| 476 | 476 | ||
| 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); |
| 479 | 479 | ||
| 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 { |
| 623 | 623 | ||
| 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); |
| 630 | 630 | ||
| 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); |
| 633 | 633 | ||
| 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..]); |
| 636 | 636 | ||
| 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 | }; |
| 659 | 659 | ||
| 660 | /// Return a non-deterministic hash of the password encoded as a PHC-format string | 660 | /// 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); |
| 670 | 669 | ||
| 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; |
| 690 | 689 | ||
| 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(); |
| 697 | 696 | ||
| 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; |
| 714 | 715 | ||
| 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); |
| 720 | 718 | ||
| ... | @@ -736,9 +734,9 @@ const CryptFormatHasher = struct { | ... | @@ -736,9 +734,9 @@ const CryptFormatHasher = struct { |
| 736 | 734 | ||
| 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; |
| 740 | 738 | ||
| 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 | }; |
| 758 | 756 | ||
| 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`. | ||
| 766 | pub fn strHash( | 769 | pub 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 | } |
| 776 | 781 | ||
| ... | @@ -796,8 +801,9 @@ pub fn strVerify( | ... | @@ -796,8 +801,9 @@ pub fn strVerify( |
| 796 | } | 801 | } |
| 797 | 802 | ||
| 798 | test "bcrypt codec" { | 803 | test "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 | } |
| 807 | 813 | ||
| 808 | test "bcrypt crypt format" { | 814 | test "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 }; |
| 814 | 822 | ||
| 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 | }; | ||
| 817 | 829 | ||
| 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 | ); |
| 824 | 836 | ||
| 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 | }; | ||
| 827 | 843 | ||
| 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" { |
| 834 | 850 | ||
| 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); |
| 839 | 859 | ||
| 840 | try strVerify( | 860 | try strVerify( |
| ... | @@ -845,15 +865,20 @@ test "bcrypt crypt format" { | ... | @@ -845,15 +865,20 @@ test "bcrypt crypt format" { |
| 845 | } | 865 | } |
| 846 | 866 | ||
| 847 | test "bcrypt phc format" { | 867 | test "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$"; |
| 854 | 875 | ||
| 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 | }; | ||
| 857 | 882 | ||
| 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 | ); |
| 864 | 889 | ||
| 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 | }; | ||
| 867 | 896 | ||
| 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" { |
| 874 | 903 | ||
| 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); |
| 879 | 912 | ||
| 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 | } |
| 324 | 324 | ||
| 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 { |
| 417 | test "Basic operations over EcdsaP384Sha384" { | 417 | test "Basic operations over EcdsaP384Sha384" { |
| 418 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | 418 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 419 | 419 | ||
| 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"; |
| 423 | 424 | ||
| 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); |
| 428 | 429 | ||
| ... | @@ -433,12 +434,13 @@ test "Basic operations over EcdsaP384Sha384" { | ... | @@ -433,12 +434,13 @@ test "Basic operations over EcdsaP384Sha384" { |
| 433 | test "Basic operations over Secp256k1" { | 434 | test "Basic operations over Secp256k1" { |
| 434 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | 435 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 435 | 436 | ||
| 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"; |
| 439 | 441 | ||
| 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); |
| 444 | 446 | ||
| ... | @@ -449,12 +451,13 @@ test "Basic operations over Secp256k1" { | ... | @@ -449,12 +451,13 @@ test "Basic operations over Secp256k1" { |
| 449 | test "Basic operations over EcdsaP384Sha256" { | 451 | test "Basic operations over EcdsaP384Sha256" { |
| 450 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | 452 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 451 | 453 | ||
| 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"; |
| 455 | 458 | ||
| 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); |
| 460 | 463 | ||
| ... | @@ -502,8 +505,10 @@ test "Verifying a existing signature with EcdsaP384Sha256" { | ... | @@ -502,8 +505,10 @@ test "Verifying a existing signature with EcdsaP384Sha256" { |
| 502 | test "Prehashed message operations" { | 505 | test "Prehashed message operations" { |
| 503 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | 506 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 504 | 507 | ||
| 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"; |
| 508 | 513 | ||
| 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)); |
| 519 | 524 | ||
| 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); |
| 524 | 529 | ||
| ... | @@ -1628,8 +1633,9 @@ fn tvTry(comptime Scheme: type, vector: TestVector) !void { | ... | @@ -1628,8 +1633,9 @@ fn tvTry(comptime Scheme: type, vector: TestVector) !void { |
| 1628 | test "Sec1 encoding/decoding" { | 1633 | test "Sec1 encoding/decoding" { |
| 1629 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | 1634 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 1630 | 1635 | ||
| 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 | } |
| 176 | 176 | ||
| 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 | } | ||
| 187 | 189 | ||
| 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; | ||
| 191 | 198 | ||
| 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 directly | 206 | @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 | } |
| 211 | 212 | ||
| 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; |
| 216 | 229 | ||
| ... | @@ -280,9 +293,9 @@ pub fn HybridKem(comptime params: Params) type { | ... | @@ -280,9 +293,9 @@ pub fn HybridKem(comptime params: Params) type { |
| 280 | } | 293 | } |
| 281 | 294 | ||
| 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); |
| 388 | 401 | ||
| 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); |
| 391 | 404 | ||
| 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()); |
| 410 | 423 | ||
| 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); |
| 414 | 427 | ||
| ... | @@ -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()); |
| 434 | 447 | ||
| 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); |
| 438 | 451 | ||
| ... | @@ -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()); |
| 458 | 471 | ||
| 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); |
| 462 | 475 | ||
| ... | @@ -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()); |
| 482 | 495 | ||
| 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); |
| 486 | 499 | ||
| ... | @@ -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()); |
| 506 | 519 | ||
| 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); |
| 510 | 523 | ||
| ... | @@ -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()); |
| 530 | 543 | ||
| 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); |
| 534 | 547 | ||
| ... | @@ -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()); |
| 554 | 567 | ||
| 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); |
| 558 | 571 | ||
| ... | @@ -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()); |
| 578 | 591 | ||
| 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); |
| 582 | 595 | ||
| ... | @@ -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()); |
| 602 | 615 | ||
| 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); |
| 606 | 619 | ||
| ... | @@ -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()); |
| 626 | 639 | ||
| 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); |
| 630 | 643 | ||
| ... | @@ -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()); |
| 650 | 663 | ||
| 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); |
| 654 | 667 | ||
| ... | @@ -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()); |
| 674 | 687 | ||
| 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); |
| 678 | 691 | ||
| ... | @@ -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()); |
| 698 | 711 | ||
| 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); |
| 702 | 715 | ||
| ... | @@ -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()); |
| 722 | 735 | ||
| 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); |
| 726 | 739 | ||
| ... | @@ -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()); |
| 746 | 759 | ||
| 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); |
| 750 | 763 | ||
| ... | @@ -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()); |
| 770 | 783 | ||
| 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); |
| 774 | 787 | ||
| ... | @@ -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()); |
| 794 | 807 | ||
| 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); |
| 798 | 811 | ||
| ... | @@ -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()); |
| 818 | 831 | ||
| 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); |
| 822 | 835 | ||
| ... | @@ -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()); |
| 842 | 855 | ||
| 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); |
| 846 | 859 | ||
| ... | @@ -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()); |
| 866 | 879 | ||
| 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); |
| 870 | 883 | ||
| ... | @@ -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()); |
| 890 | 903 | ||
| 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); |
| 894 | 907 | ||
| ... | @@ -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()); |
| 914 | 927 | ||
| 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); |
| 918 | 931 | ||
| ... | @@ -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()); |
| 938 | 951 | ||
| 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); |
| 942 | 955 | ||
| ... | @@ -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()); |
| 962 | 975 | ||
| 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); |
| 966 | 979 | ||
| ... | @@ -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()); |
| 986 | 999 | ||
| 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); |
| 990 | 1003 | ||
| ... | @@ -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()); |
| 1010 | 1023 | ||
| 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); |
| 1014 | 1027 | ||
| ... | @@ -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()); |
| 1034 | 1047 | ||
| 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); |
| 1038 | 1051 | ||
| ... | @@ -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()); |
| 1058 | 1071 | ||
| 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); |
| 1062 | 1075 | ||
| ... | @@ -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()); |
| 1082 | 1095 | ||
| 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); |
| 1086 | 1099 |
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, |
| 2020 | 2020 | ||
| 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 | } |
| 2030 | 2027 | ||
| ... | @@ -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 | } |
| 3199 | 3196 | ||
| 3200 | test "KeyPair API - generate and sign" { | 3197 | test "KeyPair API - generate and sign" { |
| 3198 | const io = std.testing.io; | ||
| 3201 | // Test the new KeyPair API with random generation | 3199 | // 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"; |
| 3204 | 3202 | ||
| 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 | } |
| 3223 | 3221 | ||
| 3224 | test "KeyPair API - fromSecretKey" { | 3222 | test "KeyPair API - fromSecretKey" { |
| 3223 | const io = std.testing.io; | ||
| 3225 | // Generate a key pair | 3224 | // Generate a key pair |
| 3226 | const kp1 = MLDSA44.KeyPair.generate(); | 3225 | const kp1 = MLDSA44.KeyPair.generate(io); |
| 3227 | 3226 | ||
| 3228 | // Derive public key from secret key | 3227 | // 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 | } |
| 3236 | 3235 | ||
| 3237 | test "Signature verification with noise" { | 3236 | test "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"; |
| 3241 | 3241 | ||
| 3242 | // Create some noise | 3242 | // Create some noise |
| ... | @@ -3250,8 +3250,9 @@ test "Signature verification with noise" { | ... | @@ -3250,8 +3250,9 @@ test "Signature verification with noise" { |
| 3250 | } | 3250 | } |
| 3251 | 3251 | ||
| 3252 | test "Signature verification failure" { | 3252 | test "Signature verification failure" { |
| 3253 | const io = std.testing.io; | ||
| 3253 | // Test that invalid signatures are rejected | 3254 | // 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); |
| 3257 | 3258 |
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; |
| 246 | 246 | ||
| 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 | } | ||
| 252 | 256 | ||
| 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 | } | ||
| 263 | 271 | ||
| 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); |
| 270 | 279 | ||
| 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]); |
| 273 | 282 | ||
| 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 | } |
| 399 | 408 | ||
| 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 | } |
| 123 | 123 | ||
| 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 | } |
| 129 | 129 |
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 | } |
| 69 | 69 | ||
| 70 | /// Return a random scalar | 70 | /// Return a random scalar |
| 71 | pub fn random(endian: std.builtin.Endian) CompressedScalar { | 71 | pub 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 | } |
| 74 | 74 | ||
| 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 | } |
| 171 | 171 | ||
| 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 | } |
| 123 | 123 | ||
| 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 | } |
| 129 | 129 |
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 | } |
| 64 | 64 | ||
| 65 | /// Return a random scalar | 65 | /// Return a random scalar |
| 66 | pub fn random(endian: std.builtin.Endian) CompressedScalar { | 66 | pub 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 | } |
| 69 | 69 | ||
| 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 | } |
| 160 | 160 | ||
| 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 | } |
| 176 | 176 | ||
| 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 | } |
| 182 | 182 |
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 | } |
| 69 | 69 | ||
| 70 | /// Return a random scalar | 70 | /// Return a random scalar |
| 71 | pub fn random(endian: std.builtin.Endian) CompressedScalar { | 71 | pub 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 | } |
| 74 | 74 | ||
| 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 | } |
| 171 | 171 | ||
| 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; |
| 5 | const P256 = @import("../p256.zig").P256; | 5 | const P256 = @import("../p256.zig").P256; |
| 6 | 6 | ||
| 7 | test "p256 ECDH key exchange" { | 7 | test "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 | } |
| 67 | 68 | ||
| 68 | test "p256 compressed sec1 encoding/decoding" { | 69 | test "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 | } |
| 74 | 76 | ||
| 75 | test "p256 uncompressed sec1 encoding/decoding" { | 77 | test "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 | } |
| 81 | 84 | ||
| 82 | test "p256 public key is the neutral element" { | 85 | test "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 | } |
| 87 | 91 | ||
| 88 | test "p256 public key is the neutral element (public verification)" { | 92 | test "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 | } |
| 93 | 98 |
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; |
| 5 | const P384 = @import("../p384.zig").P384; | 5 | const P384 = @import("../p384.zig").P384; |
| 6 | 6 | ||
| 7 | test "p384 ECDH key exchange" { | 7 | test "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 | } |
| 68 | 69 | ||
| 69 | test "p384 compressed sec1 encoding/decoding" { | 70 | test "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 | } |
| 77 | 79 | ||
| 78 | test "p384 uncompressed sec1 encoding/decoding" { | 80 | test "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 | } |
| 84 | 87 | ||
| 85 | test "p384 public key is the neutral element" { | 88 | test "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 | } |
| 90 | 94 | ||
| 91 | test "p384 public key is the neutral element (public verification)" { | 95 | test "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 | } |
| 96 | 101 |
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; |
| 5 | const Secp256k1 = @import("../secp256k1.zig").Secp256k1; | 5 | const Secp256k1 = @import("../secp256k1.zig").Secp256k1; |
| 6 | 6 | ||
| 7 | test "secp256k1 ECDH key exchange" { | 7 | test "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 | } |
| 16 | 17 | ||
| 17 | test "secp256k1 ECDH key exchange including public multiplication" { | 18 | test "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 | } |
| 78 | 80 | ||
| 79 | test "secp256k1 compressed sec1 encoding/decoding" { | 81 | test "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 | } |
| 85 | 88 | ||
| 86 | test "secp256k1 uncompressed sec1 encoding/decoding" { | 89 | test "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 | } |
| 92 | 96 | ||
| 93 | test "secp256k1 public key is the neutral element" { | 97 | test "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 | } |
| 98 | 103 | ||
| 99 | test "secp256k1 public key is the neutral element (public verification)" { | 104 | test "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 | } |
| 104 | 110 |
lib/std/crypto/salsa20.zig+19-15| ... | @@ -533,9 +533,9 @@ pub const SealedBox = struct { | ... | @@ -533,9 +533,9 @@ pub const SealedBox = struct { |
| 533 | 533 | ||
| 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 | } |
| 574 | 574 | ||
| 575 | test "xsalsa20poly1305" { | 575 | test "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); |
| 585 | 586 | ||
| 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 | } |
| 589 | 590 | ||
| 590 | test "xsalsa20poly1305 secretbox" { | 591 | test "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); |
| 599 | 601 | ||
| 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" { |
| 604 | test "xsalsa20poly1305 box" { | 606 | test "xsalsa20poly1305 box" { |
| 605 | if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/24299 | 607 | if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/24299 |
| 606 | 608 | ||
| 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); |
| 613 | 616 | ||
| 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" { |
| 620 | test "xsalsa20poly1305 sealedbox" { | 623 | test "xsalsa20poly1305 sealedbox" { |
| 621 | if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/24299 | 624 | if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/24299 |
| 622 | 625 | ||
| 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); |
| 627 | 631 | ||
| 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 | } |
| 632 | 636 |
lib/std/crypto/scrypt.zig+5-6| ... | @@ -20,7 +20,7 @@ const Error = pwhash.Error; | ... | @@ -20,7 +20,7 @@ const Error = pwhash.Error; |
| 20 | 20 | ||
| 21 | const max_size = math.maxInt(usize); | 21 | const max_size = math.maxInt(usize); |
| 22 | const max_int = max_size >> 1; | 22 | const max_int = max_size >> 1; |
| 23 | const default_salt_len = 32; | 23 | pub const default_salt_len = 32; |
| 24 | const default_hash_len = 32; | 24 | const default_hash_len = 32; |
| 25 | const max_salt_len = 64; | 25 | const max_salt_len = 64; |
| 26 | const max_hash_len = 64; | 26 | const 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); |
| 426 | 425 | ||
| ... | @@ -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); |
| 473 | 472 | ||
| 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 | } |
| 181 | 181 | ||
| 182 | test eql { | 182 | test 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 | } |
| 193 | 193 | ||
| 194 | test "eql (vectors)" { | 194 | test "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 | } |
| 221 | 221 | ||
| 222 | test "add and sub" { | 222 | test "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-b | 237 | _ = sub(u8, &a, &b, &c, endian); // a-b |
| 237 | _ = add(u8, &c, &b, &c, endian); // (a-b)+b | 238 | _ = 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 | } |
| 244 | 245 | ||
| 245 | test classify { | 246 | test 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; |
| 248 | 249 | ||
| 249 | var secret: [32]u8 = undefined; | 250 | var secret: [32]u8 = undefined; |
| 250 | random.bytes(&secret); | 251 | io.random(&secret); |
| 251 | 252 | ||
| 252 | // Input of the hash function is marked as secret | 253 | // 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 | |||
| 6 | const std = @import("std"); | ||
| 7 | const builtin = @import("builtin"); | ||
| 8 | const mem = std.mem; | ||
| 9 | const native_os = builtin.os.tag; | ||
| 10 | const posix = std.posix; | ||
| 11 | |||
| 12 | /// We use this as a layer of indirection because global const pointers cannot | ||
| 13 | /// point to thread-local variables. | ||
| 14 | pub const interface: std.Random = .{ | ||
| 15 | .ptr = undefined, | ||
| 16 | .fillFn = tlsCsprngFill, | ||
| 17 | }; | ||
| 18 | |||
| 19 | const os_has_fork = @TypeOf(posix.fork) != void; | ||
| 20 | const os_has_arc4random = builtin.link_libc and (@TypeOf(std.c.arc4random_buf) != void); | ||
| 21 | const want_fork_safety = os_has_fork and !os_has_arc4random and std.options.crypto_fork_safety; | ||
| 22 | const maybe_have_wipe_on_fork = builtin.os.isAtLeast(.linux, .{ | ||
| 23 | .major = 4, | ||
| 24 | .minor = 14, | ||
| 25 | .patch = 0, | ||
| 26 | }) orelse true; | ||
| 27 | |||
| 28 | const Rng = std.Random.DefaultCsprng; | ||
| 29 | |||
| 30 | const Context = struct { | ||
| 31 | init_state: enum(u8) { uninitialized = 0, initialized, failed }, | ||
| 32 | rng: Rng, | ||
| 33 | }; | ||
| 34 | |||
| 35 | var 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 | |||
| 45 | threadlocal var wipe_mem: []align(std.heap.page_size_min) u8 = &[_]u8{}; | ||
| 46 | |||
| 47 | fn 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 | |||
| 131 | fn setupPthreadAtforkAndFill(buffer: []u8) void { | ||
| 132 | install_atfork_handler.call(); | ||
| 133 | return initAndFill(buffer); | ||
| 134 | } | ||
| 135 | |||
| 136 | fn 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 | |||
| 143 | fn fillWithCsprng(buffer: []u8) void { | ||
| 144 | const ctx: *Context = @ptrCast(wipe_mem.ptr); | ||
| 145 | return ctx.rng.fill(buffer); | ||
| 146 | } | ||
| 147 | |||
| 148 | pub fn defaultRandomSeed(buffer: []u8) void { | ||
| 149 | posix.getrandom(buffer) catch @panic("getrandom() failed to provide entropy"); | ||
| 150 | } | ||
| 151 | |||
| 152 | fn 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 only | 110 | /// 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, |
| 115 | 115 | ||
| ... | @@ -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 | }; |
| 134 | 136 | ||
| 135 | const InitError = error{ | 137 | const 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].*; |
| 202 | 204 | ||
| 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 | ); |
| 1332 | 1334 | ||
| 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); |
| 1024 | 1024 | ||
| 1025 | // Rename should fail with PathAlreadyExists if target_dir is non-empty | 1025 | 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)); | ||
| 1027 | 1026 | ||
| 1028 | // Ensure the directory was not renamed | 1027 | // 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 file | 1650 | \\ this is a test file |
| 1652 | ; | 1651 | ; |
| 1653 | 1652 | ||
| 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; |
| 1762 | 1776 | ||
| 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); |
| 1765 | 1779 | ||
| 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 | }; |
| 503 | 503 | ||
| 504 | pub 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`. |
| 505 | pub var elf_aux_maybe: ?[*]std.elf.Auxv = null; | 514 | pub var elf_aux_maybe: ?[*]std.elf.Auxv = null; |
| 506 | 515 | ||
| ... | @@ -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 | } |
| 1354 | 1376 | ||
| ... | @@ -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 | } |
| 1375 | 1397 | ||
| 1376 | pub fn renameat2(oldfd: i32, oldpath: [*:0]const u8, newfd: i32, newpath: [*:0]const u8, flags: u32) usize { | 1398 | pub 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 | } |
| 1386 | 1408 |
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 | } |
| 533 | 533 | ||
| 534 | /// The main motivation for the size chosen here is that this is how much ends up being requested for | 534 | /// 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 up | 535 | /// 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-aligned | 536 | /// 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, I | 537 | /// 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 is | 538 | var 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. | ||
| 540 | var main_thread_area_buffer: [0x2100]u8 align(page_size_min) = undefined; | ||
| 541 | 539 | ||
| 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 | } |
| 2649 | 2649 | ||
| 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. | ||
| 2657 | pub 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 | |||
| 2706 | pub const WaitForSingleObjectError = error{ | 2650 | pub 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; |
| 3255 | 3199 | ||
| 3256 | pub fn RenameFile( | 3200 | pub 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 | } |
| 363 | 363 | ||
| 364 | pub 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. | ||
| 371 | pub 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 | |||
| 417 | fn 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 | |||
| 465 | pub const RaiseError = UnexpectedError; | 364 | pub const RaiseError = UnexpectedError; |
| 466 | 365 | ||
| 467 | pub fn raise(sig: SIG) RaiseError!void { | 366 | pub 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; |
| 1701 | 1600 | ||
| ... | @@ -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 | } |
| 35 | 35 | ||
| 36 | test "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 | |||
| 46 | test "getuid" { | 36 | test "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 { |
| 137 | 137 | ||
| 138 | fmt_max_depth: usize = fmt.default_max_depth, | 138 | fmt_max_depth: usize = fmt.default_max_depth, |
| 139 | 139 | ||
| 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` to | 140 | /// 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 { |
| 631 | pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir { | 631 | pub 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); |
| 637 | 637 |
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 |
| 3023 | 3023 | ||
| 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( |
| 5759 | 5759 | ||
| 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 | } |
| 6890 | 6894 | ||
| 6891 | pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 { | 6895 | pub 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, |
| 16 | 16 | ||
| 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); |
| 499 | 503 | ||
| 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 | } |
| 692 | 696 | ||
| 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.*; |
| 1306 | 1312 | ||
| 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); |
| 1310 | 1320 | ||
| 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 say | 1482 | // 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 { |
| 57 | 57 | ||
| 58 | pub const Error = Allocator.Error; | 58 | pub const Error = Allocator.Error; |
| 59 | 59 | ||
| 60 | pub fn parse(gpa: Allocator, ast: Ast, options: ParseOptions) Error!Manifest { | 60 | pub 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; |
| 62 | 62 | ||
| 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); |
| 89 | 89 | ||
| 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 { |
| 157 | 157 | ||
| 158 | const InnerError = error{ ParseFailure, OutOfMemory }; | 158 | const InnerError = error{ ParseFailure, OutOfMemory }; |
| 159 | 159 | ||
| 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); |
| 163 | 163 | ||
| ... | @@ -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" { |
| 623 | 623 | ||
| 624 | try testing.expect(ast.errors.len == 0); | 624 | try testing.expect(ast.errors.len == 0); |
| 625 | 625 | ||
| 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); |
| 628 | 630 | ||
| 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" { |
| 666 | 668 | ||
| 667 | try testing.expect(ast.errors.len == 0); | 669 | try testing.expect(ast.errors.len == 0); |
| 668 | 670 | ||
| 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); |
| 671 | 675 | ||
| 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" { |
| 698 | 702 | ||
| 699 | try testing.expect(ast.errors.len == 0); | 703 | try testing.expect(ast.errors.len == 0); |
| 700 | 704 | ||
| 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); |
| 703 | 709 | ||
| 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 it | 616 | // 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"; |
| 1641 | 1645 | ||
| 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"); |
| 3401 | 3401 | ||
| ... | @@ -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); |
| 4765 | 4765 | ||
| 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); | ||
| 4767 | 4768 | ||
| 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, |
| 4919 | 4920 | ||
| 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; |
| 4925 | 4926 | ||
| ... | @@ -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 then | 4938 | // 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 child | 4939 | // 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); |
| 4942 | 4943 | ||
| 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 | } |
| 7381 | 7382 | ||
| 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); |
| 7384 | 7385 | ||
| 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 | |||
| 7637 | fn 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 { |
| 10 | 10 | ||
| 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"); |
| 12 | 12 | ||
| 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; | ||
| 14 | 15 | ||
| 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 { |
| 52 | 52 | ||
| 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; |
| 5 | 5 | ||
| 6 | pub fn main(init: std.process.Init) !void { | 6 | pub 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()); |
| 9 | 10 | ||
| 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 { |
| 65 | 65 | ||
| 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); |
| 80 | 80 | ||
| 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(); |
| 102 | 102 | ||
| 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; | ||
| 455 | 456 | ||
| 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 | }; |
| 466 | 467 | ||
| 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 | |||
| 960 | fn rand64(io: Io) u64 { | ||
| 961 | var x: u64 = undefined; | ||
| 962 | io.random(@ptrCast(&x)); | ||
| 963 | return x; | ||
| 964 | } |