| 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 | 593 | .x86_64 => 3_756_422_348, |
| 594 | 594 | else => 3_800_000_000, |
| 595 | 595 | }, |
| 596 | .linux => switch (b.graph.host.result.cpu.arch) { | |
| 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 | }, | |
| 596 | .linux => 6_800_000_000, | |
| 605 | 597 | .macos => switch (b.graph.host.result.cpu.arch) { |
| 606 | 598 | .aarch64 => 8_273_795_481, |
| 607 | 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 | 1217 | } |
| 1218 | 1218 | |
| 1219 | 1219 | fn getRandomFilename(d: *Driver, buf: *[std.fs.max_name_bytes]u8, extension: []const u8) ![]const u8 { |
| 1220 | const io = d.comp.io; | |
| 1220 | 1221 | const random_bytes_count = 12; |
| 1221 | 1222 | const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count); |
| 1222 | 1223 | |
| 1223 | 1224 | var random_bytes: [random_bytes_count]u8 = undefined; |
| 1224 | std.crypto.random.bytes(&random_bytes); | |
| 1225 | io.random(&random_bytes); | |
| 1225 | 1226 | var random_name: [sub_path_len]u8 = undefined; |
| 1226 | 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 | 21 | pub const std_options: std.Options = .{ |
| 22 | 22 | .side_channels_mitigations = .none, |
| 23 | 23 | .http_disable_tls = true, |
| 24 | .crypto_fork_safety = false, | |
| 25 | 24 | }; |
| 26 | 25 | |
| 27 | 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 | 111 | pub const MakeOptions = struct { |
| 112 | 112 | progress_node: std.Progress.Node, |
| 113 | 113 | watch: bool, |
| 114 | web_server: switch (builtin.target.cpu.arch) { | |
| 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 | }, | |
| 114 | web_server: ?*Build.WebServer, | |
| 120 | 115 | /// If set, this is a timeout to enforce on all individual unit tests, in nanoseconds. |
| 121 | 116 | unit_test_timeout_ns: ?u64, |
| 122 | 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 | 1706 | // The args file is already present from a previous run. |
| 1707 | 1707 | } else |err| switch (err) { |
| 1708 | 1708 | error.FileNotFound => { |
| 1709 | try b.cache_root.handle.createDirPath(io, "tmp"); | |
| 1710 | const rand_int = std.crypto.random.int(u64); | |
| 1711 | const tmp_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); | |
| 1712 | try b.cache_root.handle.writeFile(io, .{ .sub_path = tmp_path, .data = args }); | |
| 1713 | defer b.cache_root.handle.deleteFile(io, tmp_path) catch { | |
| 1714 | // It's fine if the temporary file can't be cleaned up. | |
| 1709 | var af = b.cache_root.handle.createFileAtomic(io, args_file, .{ | |
| 1710 | .replace = false, | |
| 1711 | .make_path = true, | |
| 1712 | }) catch |e| return step.fail("failed creating tmp args file {f}{s}: {t}", .{ | |
| 1713 | b.cache_root, args_file, e, | |
| 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 | 1726 | error.PathAlreadyExists => { |
| 1718 | 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 | 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 | 476 | return; |
| 477 | 477 | } else |outer_err| switch (outer_err) { |
| 478 | 478 | error.FileNotFound => { |
| 479 | const sub_dirname = fs.path.dirname(sub_path).?; | |
| 480 | b.cache_root.handle.createDirPath(io, sub_dirname) catch |e| | |
| 481 | return step.fail("unable to make path '{f}{s}': {t}", .{ b.cache_root, sub_dirname, e }); | |
| 482 | ||
| 483 | const rand_int = std.crypto.random.int(u64); | |
| 484 | const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ | |
| 485 | std.fmt.hex(rand_int) ++ fs.path.sep_str ++ | |
| 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 | }; | |
| 479 | var atomic_file = b.cache_root.handle.createFileAtomic(io, sub_path, .{ | |
| 480 | .replace = false, | |
| 481 | .make_path = true, | |
| 482 | }) catch |err| return step.fail("failed to create temporary path for '{f}{s}': {t}", .{ | |
| 483 | b.cache_root, sub_path, err, | |
| 484 | }); | |
| 485 | defer atomic_file.deinit(io); | |
| 494 | 486 | |
| 495 | b.cache_root.handle.writeFile(io, .{ .sub_path = tmp_sub_path, .data = options.contents.items }) catch |err| { | |
| 496 | return step.fail("unable to write options to '{f}{s}': {t}", .{ | |
| 497 | b.cache_root, tmp_sub_path, err, | |
| 487 | atomic_file.file.writeStreamingAll(io, options.contents.items) catch |err| { | |
| 488 | return step.fail("failed to write options to temporary path for '{f}{s}': {t}", .{ | |
| 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 | 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 | 495 | step.result_cached = true; |
| 510 | 496 | return; |
| 511 | 497 | }, |
| 512 | else => { | |
| 513 | return step.fail("unable to rename options from '{f}{s}' to '{f}{s}': {t}", .{ | |
| 514 | b.cache_root, tmp_sub_path, | |
| 515 | b.cache_root, sub_path, | |
| 516 | err, | |
| 517 | }); | |
| 518 | }, | |
| 498 | else => return step.fail("failed to link temporary file into '{f}{s}': {t}", .{ | |
| 499 | b.cache_root, sub_path, err, | |
| 500 | }), | |
| 519 | 501 | }; |
| 520 | 502 | }, |
| 521 | 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 | 984 | }; |
| 985 | 985 | |
| 986 | 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 | 989 | const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); |
| 989 | 990 | |
| 990 | 991 | for (output_placeholders.items) |placeholder| { |
| ... | ... | @@ -1128,7 +1129,8 @@ pub fn rerunInFuzzMode( |
| 1128 | 1129 | } |
| 1129 | 1130 | |
| 1130 | 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 | 1134 | const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); |
| 1133 | 1135 | try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, .{ |
| 1134 | 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 | 293 | .tmp => { |
| 294 | 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 | 298 | const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); |
| 298 | 299 | |
| 299 | 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 | 676 | dirDeleteFile: *const fn (?*anyopaque, Dir, []const u8) Dir.DeleteFileError!void, |
| 677 | 677 | dirDeleteDir: *const fn (?*anyopaque, Dir, []const u8) Dir.DeleteDirError!void, |
| 678 | 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 | 680 | dirSymLink: *const fn (?*anyopaque, Dir, target_path: []const u8, sym_link_path: []const u8, Dir.SymLinkFlags) Dir.SymLinkError!void, |
| 680 | 681 | dirReadLink: *const fn (?*anyopaque, Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize, |
| 681 | 682 | dirSetOwner: *const fn (?*anyopaque, Dir, ?File.Uid, ?File.Gid) Dir.SetOwnerError!void, |
| ... | ... | @@ -731,6 +732,9 @@ pub const VTable = struct { |
| 731 | 732 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, |
| 732 | 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 | 738 | netListenIp: *const fn (?*anyopaque, address: net.IpAddress, net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server, |
| 735 | 739 | netAccept: *const fn (?*anyopaque, server: net.Socket.Handle) net.Server.AcceptError!net.Stream, |
| 736 | 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 | 2246 | pub fn unlockStderr(io: Io) void { |
| 2243 | 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 | 936 | pub const RenameError = error{ |
| 937 | 937 | /// In WASI, this error may occur when the file descriptor does |
| 938 | 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 | 939 | AccessDenied, |
| 940 | /// Attempted to replace a nonempty directory. | |
| 941 | DirNotEmpty, | |
| 943 | 942 | PermissionDenied, |
| 944 | 943 | FileBusy, |
| 945 | 944 | DiskQuota, |
| ... | ... | @@ -950,9 +949,8 @@ pub const RenameError = error{ |
| 950 | 949 | NotDir, |
| 951 | 950 | SystemResources, |
| 952 | 951 | NoSpaceLeft, |
| 953 | PathAlreadyExists, | |
| 954 | 952 | ReadOnlyFileSystem, |
| 955 | RenameAcrossMountPoints, | |
| 953 | CrossDevice, | |
| 956 | 954 | NoDevice, |
| 957 | 955 | SharingViolation, |
| 958 | 956 | PipeBusy, |
| ... | ... | @@ -964,6 +962,7 @@ pub const RenameError = error{ |
| 964 | 962 | /// intercepts file system operations and makes them significantly slower |
| 965 | 963 | /// in addition to possibly failing with this error code. |
| 966 | 964 | AntivirusInterference, |
| 965 | HardwareFailure, | |
| 967 | 966 | } || PathNameError || Io.Cancelable || Io.UnexpectedError; |
| 968 | 967 | |
| 969 | 968 | /// Change the name or location of a file or directory. |
| ... | ... | @@ -973,9 +972,9 @@ pub const RenameError = error{ |
| 973 | 972 | /// Renaming a file over an existing directory or a directory over an existing |
| 974 | 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/). | |
| 977 | /// 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. | |
| 975 | /// * On Windows, both paths should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | |
| 976 | /// * On WASI, both paths should be encoded as valid UTF-8. | |
| 977 | /// * On other platforms, both paths are an opaque sequence of bytes with no particular encoding. | |
| 979 | 978 | pub fn rename( |
| 980 | 979 | old_dir: Dir, |
| 981 | 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 | 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 | 1028 | pub const HardLinkOptions = File.HardLinkOptions; |
| 997 | 1029 | |
| 998 | 1030 | pub const HardLinkError = File.HardLinkError; |
| ... | ... | @@ -1098,8 +1130,10 @@ pub fn symLinkAtomic( |
| 1098 | 1130 | |
| 1099 | 1131 | const temp_path = temp_path_buf[0..temp_path_len]; |
| 1100 | 1132 | |
| 1133 | var random_integer: u64 = undefined; | |
| 1134 | ||
| 1101 | 1135 | while (true) { |
| 1102 | const random_integer = std.crypto.random.int(u64); | |
| 1136 | io.random(@ptrCast(&random_integer)); | |
| 1103 | 1137 | temp_path[dirname.len + 1 ..][0..rand_len].* = std.fmt.hex(random_integer); |
| 1104 | 1138 | |
| 1105 | 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 | 709 | } |
| 710 | 710 | |
| 711 | 711 | pub const HardLinkOptions = struct { |
| 712 | follow_symlinks: bool = true, | |
| 712 | follow_symlinks: bool = false, | |
| 713 | 713 | }; |
| 714 | 714 | |
| 715 | 715 | pub const HardLinkError = error{ |
| ... | ... | @@ -726,7 +726,7 @@ pub const HardLinkError = error{ |
| 726 | 726 | SystemResources, |
| 727 | 727 | NoSpaceLeft, |
| 728 | 728 | ReadOnlyFileSystem, |
| 729 | NotSameFileSystem, | |
| 729 | CrossDevice, | |
| 730 | 730 | NotDir, |
| 731 | 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 | 37 | af.* = undefined; |
| 38 | 38 | } |
| 39 | 39 | |
| 40 | pub const LinkError = Dir.HardLinkError; | |
| 40 | pub const LinkError = File.HardLinkError || Dir.RenamePreserveError; | |
| 41 | 41 | |
| 42 | 42 | /// Atomically materializes the file into place, failing with |
| 43 | 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 | 48 | pub fn link(af: *Atomic, io: Io) LinkError!void { |
| 45 | 49 | if (af.file_exists) { |
| 46 | 50 | if (af.file_open) { |
| ... | ... | @@ -48,8 +52,7 @@ pub fn link(af: *Atomic, io: Io) LinkError!void { |
| 48 | 52 | af.file_open = false; |
| 49 | 53 | } |
| 50 | 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, .{}); | |
| 52 | af.dir.deleteFile(io, &tmp_sub_path) catch {}; | |
| 55 | try af.dir.renamePreserve(&tmp_sub_path, af.dir, af.dest_sub_path, io); | |
| 53 | 56 | af.file_exists = false; |
| 54 | 57 | } else { |
| 55 | 58 | assert(af.file_open); |
lib/std/Io/Threaded.zig+620-50| ... | ... | @@ -65,6 +65,22 @@ argv0: Argv0, |
| 65 | 65 | environ: Environ, |
| 66 | 66 | |
| 67 | 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 | 85 | pub const Argv0 = switch (native_os) { |
| 70 | 86 | .openbsd, .haiku => struct { |
| ... | ... | @@ -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 | 179 | pub const Pid = if (native_os == .linux) enum(posix.pid_t) { |
| 155 | 180 | unknown = 0, |
| 156 | 181 | _, |
| ... | ... | @@ -585,6 +610,8 @@ const Thread = struct { |
| 585 | 610 | /// Always released when `Status.cancelation` is set to `.parked`. |
| 586 | 611 | futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn, |
| 587 | 612 | |
| 613 | csprng: Csprng, | |
| 614 | ||
| 588 | 615 | const Handle = Handle: { |
| 589 | 616 | if (std.Thread.use_pthreads) break :Handle std.c.pthread_t; |
| 590 | 617 | if (builtin.target.os.tag == .windows) break :Handle windows.HANDLE; |
| ... | ... | @@ -1285,6 +1312,7 @@ pub fn deinit(t: *Threaded) void { |
| 1285 | 1312 | if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null); |
| 1286 | 1313 | } |
| 1287 | 1314 | t.null_file.deinit(); |
| 1315 | t.random_file.deinit(); | |
| 1288 | 1316 | t.* = undefined; |
| 1289 | 1317 | } |
| 1290 | 1318 | |
| ... | ... | @@ -1313,6 +1341,7 @@ fn worker(t: *Threaded) void { |
| 1313 | 1341 | }), |
| 1314 | 1342 | .cancel_protection = .unblocked, |
| 1315 | 1343 | .futex_waiter = undefined, |
| 1344 | .csprng = .{}, | |
| 1316 | 1345 | }; |
| 1317 | 1346 | Thread.current = &thread; |
| 1318 | 1347 | |
| ... | ... | @@ -1413,6 +1442,7 @@ pub fn io(t: *Threaded) Io { |
| 1413 | 1442 | .dirDeleteFile = dirDeleteFile, |
| 1414 | 1443 | .dirDeleteDir = dirDeleteDir, |
| 1415 | 1444 | .dirRename = dirRename, |
| 1445 | .dirRenamePreserve = dirRenamePreserve, | |
| 1416 | 1446 | .dirSymLink = dirSymLink, |
| 1417 | 1447 | .dirReadLink = dirReadLink, |
| 1418 | 1448 | .dirSetOwner = dirSetOwner, |
| ... | ... | @@ -1466,6 +1496,9 @@ pub fn io(t: *Threaded) Io { |
| 1466 | 1496 | .now = now, |
| 1467 | 1497 | .sleep = sleep, |
| 1468 | 1498 | |
| 1499 | .random = random, | |
| 1500 | .randomSecure = randomSecure, | |
| 1501 | ||
| 1469 | 1502 | .netListenIp = switch (native_os) { |
| 1470 | 1503 | .windows => netListenIpWindows, |
| 1471 | 1504 | else => netListenIpPosix, |
| ... | ... | @@ -1561,6 +1594,7 @@ pub fn ioBasic(t: *Threaded) Io { |
| 1561 | 1594 | .dirDeleteFile = dirDeleteFile, |
| 1562 | 1595 | .dirDeleteDir = dirDeleteDir, |
| 1563 | 1596 | .dirRename = dirRename, |
| 1597 | .dirRenamePreserve = dirRenamePreserve, | |
| 1564 | 1598 | .dirSymLink = dirSymLink, |
| 1565 | 1599 | .dirReadLink = dirReadLink, |
| 1566 | 1600 | .dirSetOwner = dirSetOwner, |
| ... | ... | @@ -1614,6 +1648,9 @@ pub fn ioBasic(t: *Threaded) Io { |
| 1614 | 1648 | .now = now, |
| 1615 | 1649 | .sleep = sleep, |
| 1616 | 1650 | |
| 1651 | .random = random, | |
| 1652 | .randomSecure = randomSecure, | |
| 1653 | ||
| 1617 | 1654 | .netListenIp = netListenIpUnavailable, |
| 1618 | 1655 | .netListenUnix = netListenUnixUnavailable, |
| 1619 | 1656 | .netAccept = netAcceptUnavailable, |
| ... | ... | @@ -1704,6 +1741,23 @@ const linux_copy_file_range_use_c = std.c.versionCheck(if (builtin.abi.isAndroid |
| 1704 | 1741 | }); |
| 1705 | 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 | 1761 | fn async( |
| 1708 | 1762 | userdata: ?*anyopaque, |
| 1709 | 1763 | result: []u8, |
| ... | ... | @@ -2342,11 +2396,11 @@ fn dirCreateDirPath( |
| 2342 | 2396 | status = .created; |
| 2343 | 2397 | } else |err| switch (err) { |
| 2344 | 2398 | error.PathAlreadyExists => { |
| 2345 | // stat the file and return an error if it's not a directory | |
| 2346 | // this is important because otherwise a dangling symlink | |
| 2347 | // could cause an infinite loop | |
| 2348 | const fstat = try dirStatFile(t, dir, component.path, .{}); | |
| 2349 | if (fstat.kind != .directory) return error.NotDir; | |
| 2399 | // It is important to return an error if it's not a directory | |
| 2400 | // because otherwise a dangling symlink could cause an infinite | |
| 2401 | // loop. | |
| 2402 | const kind = try filePathKind(t, dir, component.path); | |
| 2403 | if (kind != .directory) return error.NotDir; | |
| 2350 | 2404 | }, |
| 2351 | 2405 | error.FileNotFound => |e| { |
| 2352 | 2406 | component = it.previous() orelse return e; |
| ... | ... | @@ -2538,11 +2592,7 @@ fn dirStatFileLinux( |
| 2538 | 2592 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 2539 | 2593 | _ = t; |
| 2540 | 2594 | const linux = std.os.linux; |
| 2541 | const use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) | |
| 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; | |
| 2595 | const sys = if (statx_use_c) std.c else std.os.linux; | |
| 2546 | 2596 | |
| 2547 | 2597 | var path_buffer: [posix.PATH_MAX]u8 = undefined; |
| 2548 | 2598 | const sub_path_posix = try pathToPosix(sub_path, &path_buffer); |
| ... | ... | @@ -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 | 2773 | fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 { |
| 2695 | 2774 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 2696 | 2775 | |
| ... | ... | @@ -2778,11 +2857,7 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 2778 | 2857 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 2779 | 2858 | _ = t; |
| 2780 | 2859 | const linux = std.os.linux; |
| 2781 | const use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) | |
| 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; | |
| 2860 | const sys = if (statx_use_c) std.c else std.os.linux; | |
| 2786 | 2861 | |
| 2787 | 2862 | const syscall: Syscall = try .start(); |
| 2788 | 2863 | while (true) { |
| ... | ... | @@ -3450,7 +3525,7 @@ fn dirCreateFileAtomic( |
| 3450 | 3525 | if (dest_dirname) |dirname| { |
| 3451 | 3526 | // This has a nice side effect of preemptively triggering EISDIR or |
| 3452 | 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 | 3529 | // None of these make sense in this context. |
| 3455 | 3530 | error.IsDir, |
| 3456 | 3531 | error.Streaming, |
| ... | ... | @@ -3553,8 +3628,9 @@ fn atomicFileInit( |
| 3553 | 3628 | dir: Dir, |
| 3554 | 3629 | close_dir_on_deinit: bool, |
| 3555 | 3630 | ) Dir.CreateFileAtomicError!File.Atomic { |
| 3631 | var random_integer: u64 = undefined; | |
| 3556 | 3632 | while (true) { |
| 3557 | const random_integer = std.crypto.random.int(u64); | |
| 3633 | t_io.random(@ptrCast(&random_integer)); | |
| 3558 | 3634 | const tmp_sub_path = std.fmt.hex(random_integer); |
| 3559 | 3635 | const file = dir.createFile(t_io, &tmp_sub_path, .{ |
| 3560 | 3636 | .permissions = permissions, |
| ... | ... | @@ -3636,10 +3712,12 @@ fn dirOpenFilePosix( |
| 3636 | 3712 | }, |
| 3637 | 3713 | }; |
| 3638 | 3714 | |
| 3715 | const mode: posix.mode_t = 0; | |
| 3716 | ||
| 3639 | 3717 | const fd: posix.fd_t = fd: { |
| 3640 | 3718 | const syscall: Syscall = try .start(); |
| 3641 | 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 | 3721 | switch (posix.errno(rc)) { |
| 3644 | 3722 | .SUCCESS => { |
| 3645 | 3723 | syscall.finish(); |
| ... | ... | @@ -4068,9 +4146,11 @@ fn dirOpenDirPosix( |
| 4068 | 4146 | if (@hasField(posix.O, "PATH") and !options.iterate) |
| 4069 | 4147 | flags.PATH = true; |
| 4070 | 4148 | |
| 4149 | const mode: posix.mode_t = 0; | |
| 4150 | ||
| 4071 | 4151 | const syscall: Syscall = try .start(); |
| 4072 | 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 | 4154 | switch (posix.errno(rc)) { |
| 4075 | 4155 | .SUCCESS => { |
| 4076 | 4156 | syscall.finish(); |
| ... | ... | @@ -5169,12 +5249,21 @@ fn fileHardLink( |
| 5169 | 5249 | var new_path_buffer: [posix.PATH_MAX]u8 = undefined; |
| 5170 | 5250 | const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer); |
| 5171 | 5251 | |
| 5172 | const flags: u32 = if (!options.follow_symlinks) | |
| 5173 | posix.AT.SYMLINK_NOFOLLOW | posix.AT.EMPTY_PATH | |
| 5252 | const flags: u32 = if (options.follow_symlinks) | |
| 5253 | posix.AT.SYMLINK_FOLLOW | posix.AT.EMPTY_PATH | |
| 5174 | 5254 | else |
| 5175 | 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 | 5269 | fn linkat( |
| ... | ... | @@ -5205,7 +5294,7 @@ fn linkat( |
| 5205 | 5294 | .NOTDIR => return syscall.fail(error.NotDir), |
| 5206 | 5295 | .PERM => return syscall.fail(error.PermissionDenied), |
| 5207 | 5296 | .ROFS => return syscall.fail(error.ReadOnlyFileSystem), |
| 5208 | .XDEV => return syscall.fail(error.NotSameFileSystem), | |
| 5297 | .XDEV => return syscall.fail(error.CrossDevice), | |
| 5209 | 5298 | .ILSEQ => return syscall.fail(error.BadPathName), |
| 5210 | 5299 | .FAULT => |err| return syscall.errnoBug(err), |
| 5211 | 5300 | .INVAL => |err| return syscall.errnoBug(err), |
| ... | ... | @@ -5614,15 +5703,44 @@ fn dirRenameWindows( |
| 5614 | 5703 | new_dir: Dir, |
| 5615 | 5704 | new_sub_path: []const u8, |
| 5616 | 5705 | ) Dir.RenameError!void { |
| 5617 | const w = windows; | |
| 5618 | 5706 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 5619 | 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 | 5740 | const old_path_w_buf = try windows.sliceToPrefixedFileW(old_dir.handle, old_sub_path); |
| 5622 | 5741 | const old_path_w = old_path_w_buf.span(); |
| 5623 | 5742 | const new_path_w_buf = try windows.sliceToPrefixedFileW(new_dir.handle, new_sub_path); |
| 5624 | 5743 | const new_path_w = new_path_w_buf.span(); |
| 5625 | const replace_if_exists = true; | |
| 5626 | 5744 | |
| 5627 | 5745 | const src_fd = src_fd: { |
| 5628 | 5746 | const syscall: Syscall = try .start(); |
| ... | ... | @@ -5724,9 +5842,9 @@ fn dirRenameWindows( |
| 5724 | 5842 | .ACCESS_DENIED => return error.AccessDenied, |
| 5725 | 5843 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, |
| 5726 | 5844 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, |
| 5727 | .NOT_SAME_DEVICE => return error.RenameAcrossMountPoints, | |
| 5845 | .NOT_SAME_DEVICE => return error.CrossDevice, | |
| 5728 | 5846 | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, |
| 5729 | .DIRECTORY_NOT_EMPTY => return error.PathAlreadyExists, | |
| 5847 | .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty, | |
| 5730 | 5848 | .FILE_IS_A_DIRECTORY => return error.IsDir, |
| 5731 | 5849 | .NOT_A_DIRECTORY => return error.NotDir, |
| 5732 | 5850 | else => return w.unexpectedStatus(rc), |
| ... | ... | @@ -5770,10 +5888,10 @@ fn dirRenameWasi( |
| 5770 | 5888 | .NOTDIR => return error.NotDir, |
| 5771 | 5889 | .NOMEM => return error.SystemResources, |
| 5772 | 5890 | .NOSPC => return error.NoSpaceLeft, |
| 5773 | .EXIST => return error.PathAlreadyExists, | |
| 5774 | .NOTEMPTY => return error.PathAlreadyExists, | |
| 5891 | .EXIST => return error.DirNotEmpty, | |
| 5892 | .NOTEMPTY => return error.DirNotEmpty, | |
| 5775 | 5893 | .ROFS => return error.ReadOnlyFileSystem, |
| 5776 | .XDEV => return error.RenameAcrossMountPoints, | |
| 5894 | .XDEV => return error.CrossDevice, | |
| 5777 | 5895 | .NOTCAPABLE => return error.AccessDenied, |
| 5778 | 5896 | .ILSEQ => return error.BadPathName, |
| 5779 | 5897 | else => |err| return posix.unexpectedErrno(err), |
| ... | ... | @@ -5799,9 +5917,105 @@ fn dirRenamePosix( |
| 5799 | 5917 | const old_sub_path_posix = try pathToPosix(old_sub_path, &old_path_buffer); |
| 5800 | 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 | 6016 | const syscall: Syscall = try .start(); |
| 5803 | 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 | 6019 | .SUCCESS => return syscall.finish(), |
| 5806 | 6020 | .INTR => { |
| 5807 | 6021 | try syscall.checkCancel(); |
| ... | ... | @@ -5827,7 +6041,7 @@ fn dirRenamePosix( |
| 5827 | 6041 | .EXIST => return error.PathAlreadyExists, |
| 5828 | 6042 | .NOTEMPTY => return error.PathAlreadyExists, |
| 5829 | 6043 | .ROFS => return error.ReadOnlyFileSystem, |
| 5830 | .XDEV => return error.RenameAcrossMountPoints, | |
| 6044 | .XDEV => return error.CrossDevice, | |
| 5831 | 6045 | .ILSEQ => return error.BadPathName, |
| 5832 | 6046 | else => |err| return posix.unexpectedErrno(err), |
| 5833 | 6047 | } |
| ... | ... | @@ -6318,11 +6532,6 @@ fn fchmodatFallback( |
| 6318 | 6532 | mode: posix.mode_t, |
| 6319 | 6533 | ) Dir.SetFilePermissionsError!void { |
| 6320 | 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 | 6536 | // Fallback to changing permissions using procfs: |
| 6328 | 6537 | // |
| ... | ... | @@ -6369,6 +6578,7 @@ fn fchmodatFallback( |
| 6369 | 6578 | defer posix.close(path_fd); |
| 6370 | 6579 | |
| 6371 | 6580 | const path_mode = mode: { |
| 6581 | const sys = if (statx_use_c) std.c else std.os.linux; | |
| 6372 | 6582 | const syscall: Syscall = try .start(); |
| 6373 | 6583 | while (true) { |
| 6374 | 6584 | var statx = std.mem.zeroes(std.os.linux.Statx); |
| ... | ... | @@ -7612,7 +7822,7 @@ fn dirHardLink( |
| 7612 | 7822 | .NOTDIR => return error.NotDir, |
| 7613 | 7823 | .PERM => return error.PermissionDenied, |
| 7614 | 7824 | .ROFS => return error.ReadOnlyFileSystem, |
| 7615 | .XDEV => return error.NotSameFileSystem, | |
| 7825 | .XDEV => return error.CrossDevice, | |
| 7616 | 7826 | .INVAL => |err| return errnoBug(err), |
| 7617 | 7827 | .ILSEQ => return error.BadPathName, |
| 7618 | 7828 | else => |err| return posix.unexpectedErrno(err), |
| ... | ... | @@ -7628,7 +7838,7 @@ fn dirHardLink( |
| 7628 | 7838 | const old_sub_path_posix = try pathToPosix(old_sub_path, &old_path_buffer); |
| 7629 | 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 | 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 | 12478 | .nlink = stx.nlink, |
| 12269 | 12479 | .size = stx.size, |
| 12270 | 12480 | .permissions = .fromMode(stx.mode), |
| 12271 | .kind = switch (stx.mode & std.os.linux.S.IFMT) { | |
| 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 | }, | |
| 12481 | .kind = statxKind(stx.mode), | |
| 12281 | 12482 | .atime = if (!stx.mask.ATIME) null else .{ |
| 12282 | 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 | 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 | 12503 | fn statFromPosix(st: *const posix.Stat) File.Stat { |
| 12290 | 12504 | const atime = st.atime(); |
| 12291 | 12505 | const mtime = st.mtime(); |
| ... | ... | @@ -12441,7 +12655,8 @@ fn lookupDns( |
| 12441 | 12655 | |
| 12442 | 12656 | for (family_records) |fr| { |
| 12443 | 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 | 12660 | const len = writeResolutionQuery(&query_buffers[nq], 0, lookup_canon_name, 1, fr.rr, entropy); |
| 12446 | 12661 | queries_buffer[nq] = query_buffers[nq][0..len]; |
| 12447 | 12662 | nq += 1; |
| ... | ... | @@ -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 | 14127 | fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 13857 | 14128 | { |
| 13858 | 14129 | t.mutex.lock(); |
| ... | ... | @@ -14935,6 +15206,305 @@ pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 { |
| 14935 | 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 | 15508 | test { |
| 14939 | 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 | 275 | |
| 276 | 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 | 279 | defer testing.allocator.free(socket_path); |
| 280 | 280 | |
| 281 | 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 | 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 | 312 | const random_bytes_count = 12; |
| 313 | 313 | const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count); |
| 314 | 314 | var random_bytes: [12]u8 = undefined; |
| 315 | std.crypto.random.bytes(&random_bytes); | |
| 315 | io.random(&random_bytes); | |
| 316 | 316 | var sub_path: [sub_path_len]u8 = undefined; |
| 317 | 317 | _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); |
| 318 | 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 | 564 | try io.sleep(.fromMilliseconds(10), .awake); // let that first sleep start up |
| 565 | 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 | 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 | 2 | //! Be sure to use a CSPRNG when required, otherwise using a normal PRNG will |
| 5 | 3 | //! be faster and use substantially less stack space. |
| 4 | const Random = @This(); | |
| 6 | 5 | |
| 7 | 6 | const std = @import("std.zig"); |
| 8 | 7 | const math = std.math; |
| 9 | 8 | const mem = std.mem; |
| 10 | 9 | const assert = std.debug.assert; |
| 11 | 10 | const maxInt = std.math.maxInt; |
| 12 | const Random = @This(); | |
| 13 | 11 | |
| 14 | 12 | /// Fast unbiased random numbers. |
| 15 | 13 | pub const DefaultPrng = Xoshiro256; |
| ... | ... | @@ -35,6 +33,22 @@ pub const ziggurat = @import("Random/ziggurat.zig"); |
| 35 | 33 | ptr: *anyopaque, |
| 36 | 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 | 52 | pub fn init(pointer: anytype, comptime fillFn: fn (ptr: @TypeOf(pointer), buf: []u8) void) Random { |
| 39 | 53 | const Ptr = @TypeOf(pointer); |
| 40 | 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 | 20 | |
| 21 | 21 | /// The seed must be uniform, secret and `secret_seed_length` bytes long. |
| 22 | 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 | 24 | Cipher.stream(&self.state, 0, secret_seed, nonce); |
| 25 | 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 | 436 | } |
| 437 | 437 | |
| 438 | 438 | test "CSPRNG" { |
| 439 | const io = std.testing.io; | |
| 439 | 440 | var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined; |
| 440 | std.crypto.random.bytes(&secret_seed); | |
| 441 | io.random(&secret_seed); | |
| 441 | 442 | var csprng = DefaultCsprng.init(secret_seed); |
| 442 | 443 | const random = csprng.random(); |
| 443 | 444 | const a = random.int(u64); |
lib/std/crypto.zig+4-11| ... | ... | @@ -235,9 +235,6 @@ pub const nacl = struct { |
| 235 | 235 | /// Finite-field arithmetic. |
| 236 | 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 | 238 | /// Encoding and decoding |
| 242 | 239 | pub const codecs = @import("crypto/codecs.zig"); |
| 243 | 240 | |
| ... | ... | @@ -306,6 +303,9 @@ test { |
| 306 | 303 | _ = dh.X25519; |
| 307 | 304 | |
| 308 | 305 | _ = kem.kyber_d00; |
| 306 | _ = kem.hybrid; | |
| 307 | _ = kem.kyber_d00; | |
| 308 | _ = kem.ml_kem; | |
| 309 | 309 | |
| 310 | 310 | _ = ecc.Curve25519; |
| 311 | 311 | _ = ecc.Edwards25519; |
| ... | ... | @@ -343,6 +343,7 @@ test { |
| 343 | 343 | |
| 344 | 344 | _ = sign.Ed25519; |
| 345 | 345 | _ = sign.ecdsa; |
| 346 | _ = sign.mldsa; | |
| 346 | 347 | |
| 347 | 348 | _ = stream.chacha.ChaCha20IETF; |
| 348 | 349 | _ = stream.chacha.ChaCha12IETF; |
| ... | ... | @@ -364,20 +365,12 @@ test { |
| 364 | 365 | _ = secureZero; |
| 365 | 366 | _ = timing_safe; |
| 366 | 367 | _ = ff; |
| 367 | _ = random; | |
| 368 | 368 | _ = errors; |
| 369 | 369 | _ = tls; |
| 370 | 370 | _ = Certificate; |
| 371 | 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 | 374 | test "issue #4532: no index out of bounds" { |
| 382 | 375 | const types = [_]type{ |
| 383 | 376 | hash.Md5, |
lib/std/crypto/25519/ed25519.zig+32-23| ... | ... | @@ -333,12 +333,10 @@ pub const Ed25519 = struct { |
| 333 | 333 | } |
| 334 | 334 | |
| 335 | 335 | /// Generate a new, random key pair. |
| 336 | /// | |
| 337 | /// `crypto.random.bytes` must be supported by the target. | |
| 338 | pub fn generate() KeyPair { | |
| 336 | pub fn generate(io: std.Io) KeyPair { | |
| 339 | 337 | var random_seed: [seed_length]u8 = undefined; |
| 340 | 338 | while (true) { |
| 341 | crypto.random.bytes(&random_seed); | |
| 339 | io.random(&random_seed); | |
| 342 | 340 | return generateDeterministic(random_seed) catch { |
| 343 | 341 | @branchHint(.unlikely); |
| 344 | 342 | continue; |
| ... | ... | @@ -389,18 +387,21 @@ pub const Ed25519 = struct { |
| 389 | 387 | |
| 390 | 388 | /// Create a Signer, that can be used for incremental signing. |
| 391 | 389 | /// Note that the signature is not deterministic. |
| 392 | /// The noise parameter, if set, should be something unique for each message, | |
| 393 | /// such as a random nonce, or a counter. | |
| 394 | pub fn signer(key_pair: KeyPair, noise: ?[noise_length]u8) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer { | |
| 390 | pub fn signer( | |
| 391 | key_pair: KeyPair, | |
| 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 | 398 | if (!mem.eql(u8, &key_pair.secret_key.publicKeyBytes(), &key_pair.public_key.toBytes())) { |
| 396 | 399 | return error.KeyMismatch; |
| 397 | 400 | } |
| 398 | 401 | const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix(); |
| 399 | 402 | var h = Sha512.init(.{}); |
| 400 | 403 | h.update(&scalar_and_prefix.prefix); |
| 401 | var noise2: [noise_length]u8 = undefined; | |
| 402 | crypto.random.bytes(&noise2); | |
| 403 | h.update(&noise2); | |
| 404 | h.update(entropy); | |
| 404 | 405 | if (noise) |*z| { |
| 405 | 406 | h.update(z); |
| 406 | 407 | } |
| ... | ... | @@ -420,7 +421,7 @@ pub const Ed25519 = struct { |
| 420 | 421 | }; |
| 421 | 422 | |
| 422 | 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 | 425 | var r_batch: [count]CompressedScalar = undefined; |
| 425 | 426 | var s_batch: [count]CompressedScalar = undefined; |
| 426 | 427 | var a_batch: [count]Curve = undefined; |
| ... | ... | @@ -454,7 +455,7 @@ pub const Ed25519 = struct { |
| 454 | 455 | |
| 455 | 456 | var z_batch: [count]Curve.scalar.CompressedScalar = undefined; |
| 456 | 457 | for (&z_batch) |*z| { |
| 457 | crypto.random.bytes(z[0..16]); | |
| 458 | io.random(z[0..16]); | |
| 458 | 459 | @memset(z[16..], 0); |
| 459 | 460 | } |
| 460 | 461 | |
| ... | ... | @@ -587,12 +588,14 @@ test "signature" { |
| 587 | 588 | } |
| 588 | 589 | |
| 589 | 590 | test "batch verification" { |
| 591 | const io = std.testing.io; | |
| 592 | ||
| 590 | 593 | for (0..16) |_| { |
| 591 | const key_pair = Ed25519.KeyPair.generate(); | |
| 594 | const key_pair = Ed25519.KeyPair.generate(io); | |
| 592 | 595 | var msg1: [32]u8 = undefined; |
| 593 | 596 | var msg2: [32]u8 = undefined; |
| 594 | crypto.random.bytes(&msg1); | |
| 595 | crypto.random.bytes(&msg2); | |
| 597 | io.random(&msg1); | |
| 598 | io.random(&msg2); | |
| 596 | 599 | const sig1 = try key_pair.sign(&msg1, null); |
| 597 | 600 | const sig2 = try key_pair.sign(&msg2, null); |
| 598 | 601 | var signature_batch = [_]Ed25519.BatchElement{ |
| ... | ... | @@ -607,10 +610,10 @@ test "batch verification" { |
| 607 | 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 | 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 | 721 | } |
| 719 | 722 | |
| 720 | 723 | test "with blind keys" { |
| 724 | const io = std.testing.io; | |
| 721 | 725 | const BlindKeyPair = Ed25519.key_blinding.BlindKeyPair; |
| 722 | 726 | |
| 723 | 727 | // Create a standard Ed25519 key pair |
| 724 | const kp = Ed25519.KeyPair.generate(); | |
| 728 | const kp = Ed25519.KeyPair.generate(io); | |
| 725 | 729 | |
| 726 | 730 | // Create a random blinding seed |
| 727 | 731 | var blind: [32]u8 = undefined; |
| 728 | crypto.random.bytes(&blind); | |
| 732 | io.random(&blind); | |
| 729 | 733 | |
| 730 | 734 | // Blind the key pair |
| 731 | 735 | const blind_kp = try BlindKeyPair.init(kp, blind, "ctx"); |
| ... | ... | @@ -741,9 +745,12 @@ test "with blind keys" { |
| 741 | 745 | } |
| 742 | 746 | |
| 743 | 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 | 754 | signer.update("mes"); |
| 748 | 755 | signer.update("sage"); |
| 749 | 756 | const sig = signer.finalize(); |
| ... | ... | @@ -757,7 +764,8 @@ test "signatures with streaming" { |
| 757 | 764 | } |
| 758 | 765 | |
| 759 | 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 | 769 | const kp2 = try Ed25519.KeyPair.fromSecretKey(kp.secret_key); |
| 762 | 770 | try std.testing.expectEqualSlices(u8, &kp.secret_key.toBytes(), &kp2.secret_key.toBytes()); |
| 763 | 771 | try std.testing.expectEqualSlices(u8, &kp.public_key.toBytes(), &kp2.public_key.toBytes()); |
| ... | ... | @@ -788,7 +796,8 @@ test "cofactored vs cofactorless verification" { |
| 788 | 796 | } |
| 789 | 797 | |
| 790 | 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 | 801 | const msg = "test message"; |
| 793 | 802 | const sig = try kp.sign(msg, null); |
| 794 | 803 | try sig.verify(msg, kp.public_key); |
lib/std/crypto/25519/edwards25519.zig+5-3| ... | ... | @@ -575,10 +575,11 @@ test "packing/unpacking" { |
| 575 | 575 | } |
| 576 | 576 | |
| 577 | 577 | test "point addition/subtraction" { |
| 578 | const io = std.testing.io; | |
| 578 | 579 | var s1: [32]u8 = undefined; |
| 579 | 580 | var s2: [32]u8 = undefined; |
| 580 | crypto.random.bytes(&s1); | |
| 581 | crypto.random.bytes(&s2); | |
| 581 | io.random(&s1); | |
| 582 | io.random(&s2); | |
| 582 | 583 | const p = try Edwards25519.basePoint.clampedMul(s1); |
| 583 | 584 | const q = try Edwards25519.basePoint.clampedMul(s2); |
| 584 | 585 | const r = p.add(q).add(q).sub(q).sub(q); |
| ... | ... | @@ -622,9 +623,10 @@ test "implicit reduction of invalid scalars" { |
| 622 | 623 | } |
| 623 | 624 | |
| 624 | 625 | test "subgroup check" { |
| 626 | const io = std.testing.io; | |
| 625 | 627 | for (0..100) |_| { |
| 626 | 628 | var p = Edwards25519.basePoint; |
| 627 | const s = Edwards25519.scalar.random(); | |
| 629 | const s = Edwards25519.scalar.random(io); | |
| 628 | 630 | p = try p.mulPublic(s); |
| 629 | 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 | 101 | } |
| 102 | 102 | |
| 103 | 103 | /// Return a random scalar < L |
| 104 | pub fn random() CompressedScalar { | |
| 105 | return Scalar.random().toBytes(); | |
| 104 | pub fn random(io: std.Io) CompressedScalar { | |
| 105 | return Scalar.random(io).toBytes(); | |
| 106 | 106 | } |
| 107 | 107 | |
| 108 | 108 | /// A scalar in unpacked representation |
| ... | ... | @@ -560,10 +560,10 @@ pub const Scalar = struct { |
| 560 | 560 | } |
| 561 | 561 | |
| 562 | 562 | /// Return a random scalar < L. |
| 563 | pub fn random() Scalar { | |
| 563 | pub fn random(io: std.Io) Scalar { | |
| 564 | 564 | var s: [64]u8 = undefined; |
| 565 | 565 | while (true) { |
| 566 | crypto.random.bytes(&s); | |
| 566 | io.random(&s); | |
| 567 | 567 | const n = Scalar.fromBytes64(s); |
| 568 | 568 | if (!n.isZero()) { |
| 569 | 569 | return n; |
| ... | ... | @@ -879,8 +879,9 @@ test "scalar field inversion" { |
| 879 | 879 | } |
| 880 | 880 | |
| 881 | 881 | test "random scalar" { |
| 882 | const s1 = random(); | |
| 883 | const s2 = random(); | |
| 882 | const io = std.testing.io; | |
| 883 | const s1 = random(io); | |
| 884 | const s2 = random(io); | |
| 884 | 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 | 41 | } |
| 42 | 42 | |
| 43 | 43 | /// Generate a new, random key pair. |
| 44 | pub fn generate() KeyPair { | |
| 44 | pub fn generate(io: std.Io) KeyPair { | |
| 45 | 45 | var random_seed: [seed_length]u8 = undefined; |
| 46 | 46 | while (true) { |
| 47 | crypto.random.bytes(&random_seed); | |
| 47 | io.random(&random_seed); | |
| 48 | 48 | return generateDeterministic(random_seed) catch { |
| 49 | 49 | @branchHint(.unlikely); |
| 50 | 50 | continue; |
lib/std/crypto/argon2.zig+1-1| ... | ... | @@ -533,7 +533,7 @@ const PhcFormatHasher = struct { |
| 533 | 533 | if (params.secret != null or params.ad != null) return HasherError.InvalidEncoding; |
| 534 | 534 | |
| 535 | 535 | var salt: [default_salt_len]u8 = undefined; |
| 536 | crypto.random.bytes(&salt); | |
| 536 | io.random(&salt); | |
| 537 | 537 | |
| 538 | 538 | var hash: [default_hash_len]u8 = undefined; |
| 539 | 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 | 17 | const EncodingError = phc_format.Error; |
| 18 | 18 | const Error = pwhash.Error; |
| 19 | 19 | |
| 20 | const salt_length: usize = 16; | |
| 20 | pub const salt_length: usize = 16; | |
| 21 | 21 | const salt_str_length: usize = 22; |
| 22 | 22 | const ct_str_length: usize = 31; |
| 23 | 23 | const ct_length: usize = 24; |
| ... | ... | @@ -426,7 +426,7 @@ pub const Params = struct { |
| 426 | 426 | |
| 427 | 427 | fn bcryptWithTruncation( |
| 428 | 428 | password: []const u8, |
| 429 | salt: [salt_length]u8, | |
| 429 | salt: *const [salt_length]u8, | |
| 430 | 430 | params: Params, |
| 431 | 431 | ) [dk_length]u8 { |
| 432 | 432 | var state = State{}; |
| ... | ... | @@ -435,13 +435,13 @@ fn bcryptWithTruncation( |
| 435 | 435 | @memcpy(password_buf[0..trimmed_len], password[0..trimmed_len]); |
| 436 | 436 | password_buf[trimmed_len] = 0; |
| 437 | 437 | const passwordZ = password_buf[0 .. trimmed_len + 1]; |
| 438 | state.expand(salt[0..], passwordZ); | |
| 438 | state.expand(salt, passwordZ); | |
| 439 | 439 | |
| 440 | 440 | const rounds: u64 = @as(u64, 1) << params.rounds_log; |
| 441 | 441 | var k: u64 = 0; |
| 442 | 442 | while (k < rounds) : (k += 1) { |
| 443 | 443 | state.expand0(passwordZ); |
| 444 | state.expand0(salt[0..]); | |
| 444 | state.expand0(salt); | |
| 445 | 445 | } |
| 446 | 446 | crypto.secureZero(u8, &password_buf); |
| 447 | 447 | |
| ... | ... | @@ -467,7 +467,7 @@ fn bcryptWithTruncation( |
| 467 | 467 | /// For key derivation, use `bcrypt.pbkdf()` or `bcrypt.opensshKdf()` instead. |
| 468 | 468 | pub fn bcrypt( |
| 469 | 469 | password: []const u8, |
| 470 | salt: [salt_length]u8, | |
| 470 | salt: *const [salt_length]u8, | |
| 471 | 471 | params: Params, |
| 472 | 472 | ) [dk_length]u8 { |
| 473 | 473 | if (password.len <= 72 or params.silently_truncate_password) { |
| ... | ... | @@ -475,7 +475,7 @@ pub fn bcrypt( |
| 475 | 475 | } |
| 476 | 476 | |
| 477 | 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 | 480 | const Encoder = crypt_format.Codec.Encoder; |
| 481 | 481 | var pre_hash_b64: [Encoder.calcSize(pre_hash.len)]u8 = undefined; |
| ... | ... | @@ -623,16 +623,16 @@ const crypt_format = struct { |
| 623 | 623 | |
| 624 | 624 | fn strHashInternal( |
| 625 | 625 | password: []const u8, |
| 626 | salt: [salt_length]u8, | |
| 626 | salt: *const [salt_length]u8, | |
| 627 | 627 | params: Params, |
| 628 | 628 | ) [hash_length]u8 { |
| 629 | 629 | var dk = bcrypt(password, salt, params); |
| 630 | 630 | |
| 631 | 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 | 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 | 637 | var s_buf: [hash_length]u8 = undefined; |
| 638 | 638 | const s = fmt.bufPrint( |
| ... | ... | @@ -657,21 +657,20 @@ const PhcFormatHasher = struct { |
| 657 | 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 | 661 | fn create( |
| 662 | 662 | password: []const u8, |
| 663 | 663 | params: Params, |
| 664 | 664 | buf: []u8, |
| 665 | /// Filled with cryptographically secure entropy. | |
| 666 | salt: *const [salt_length]u8, | |
| 665 | 667 | ) HasherError![]const u8 { |
| 666 | var salt: [salt_length]u8 = undefined; | |
| 667 | crypto.random.bytes(&salt); | |
| 668 | ||
| 669 | 668 | const hash = bcrypt(password, salt, params); |
| 670 | 669 | |
| 671 | 670 | return phc_format.serialize(HashResult{ |
| 672 | 671 | .alg_id = alg_id, |
| 673 | 672 | .r = params.rounds_log, |
| 674 | .salt = try BinValue(salt_length).fromSlice(&salt), | |
| 673 | .salt = try BinValue(salt_length).fromSlice(salt), | |
| 675 | 674 | .hash = try BinValue(dk_length).fromSlice(&hash), |
| 676 | 675 | }, buf); |
| 677 | 676 | } |
| ... | ... | @@ -688,11 +687,11 @@ const PhcFormatHasher = struct { |
| 688 | 687 | if (hash_result.salt.len != salt_length or hash_result.hash.len != dk_length) |
| 689 | 688 | return HasherError.InvalidEncoding; |
| 690 | 689 | |
| 691 | const params = Params{ | |
| 690 | const params: Params = .{ | |
| 692 | 691 | .rounds_log = hash_result.r, |
| 693 | 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 | 695 | const expected_hash = hash_result.hash.constSlice(); |
| 697 | 696 | |
| 698 | 697 | if (!mem.eql(u8, &hash, expected_hash)) return HasherError.PasswordVerificationFailed; |
| ... | ... | @@ -709,12 +708,11 @@ const CryptFormatHasher = struct { |
| 709 | 708 | password: []const u8, |
| 710 | 709 | params: Params, |
| 711 | 710 | buf: []u8, |
| 711 | /// Filled with cryptographically secure entropy. | |
| 712 | salt: *const [salt_length]u8, | |
| 712 | 713 | ) HasherError![]const u8 { |
| 713 | 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 | 716 | const hash = crypt_format.strHashInternal(password, salt, params); |
| 719 | 717 | @memcpy(buf[0..hash.len], &hash); |
| 720 | 718 | |
| ... | ... | @@ -736,9 +734,9 @@ const CryptFormatHasher = struct { |
| 736 | 734 | |
| 737 | 735 | const salt_str = str[7..][0..salt_str_length]; |
| 738 | 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 | 740 | .rounds_log = rounds_log, |
| 743 | 741 | .silently_truncate_password = silently_truncate_password, |
| 744 | 742 | }); |
| ... | ... | @@ -756,21 +754,28 @@ pub const HashOptions = struct { |
| 756 | 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. | |
| 760 | /// bcrypt is a computationally expensive and cache-hard function, explicitly designed to slow down exhaustive searches. | |
| 757 | /// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key | |
| 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. | |
| 765 | /// If this is an issue for your application, set the `silently_truncate_password` option to `false`. | |
| 766 | /// By design, bcrypt silently truncates passwords to 72 bytes. If this is an | |
| 767 | /// issue for your application, set the `silently_truncate_password` option to | |
| 768 | /// `false`. | |
| 766 | 769 | pub fn strHash( |
| 767 | 770 | password: []const u8, |
| 768 | 771 | options: HashOptions, |
| 769 | 772 | out: []u8, |
| 773 | /// Filled with cryptographically secure entropy. | |
| 774 | salt: *const [salt_length]u8, | |
| 770 | 775 | ) Error![]const u8 { |
| 771 | 776 | switch (options.encoding) { |
| 772 | .phc => return PhcFormatHasher.create(password, options.params, out), | |
| 773 | .crypt => return CryptFormatHasher.create(password, options.params, out), | |
| 777 | .phc => return PhcFormatHasher.create(password, options.params, out, salt), | |
| 778 | .crypt => return CryptFormatHasher.create(password, options.params, out, salt), | |
| 774 | 779 | } |
| 775 | 780 | } |
| 776 | 781 | |
| ... | ... | @@ -796,8 +801,9 @@ pub fn strVerify( |
| 796 | 801 | } |
| 797 | 802 | |
| 798 | 803 | test "bcrypt codec" { |
| 804 | const io = testing.io; | |
| 799 | 805 | var salt: [salt_length]u8 = undefined; |
| 800 | crypto.random.bytes(&salt); | |
| 806 | io.random(&salt); | |
| 801 | 807 | var salt_str: [salt_str_length]u8 = undefined; |
| 802 | 808 | _ = crypt_format.Codec.Encoder.encode(salt_str[0..], salt[0..]); |
| 803 | 809 | var salt2: [salt_length]u8 = undefined; |
| ... | ... | @@ -806,14 +812,20 @@ test "bcrypt codec" { |
| 806 | 812 | } |
| 807 | 813 | |
| 808 | 814 | test "bcrypt crypt format" { |
| 809 | var hash_options = HashOptions{ | |
| 815 | const io = testing.io; | |
| 816 | ||
| 817 | var hash_options: HashOptions = .{ | |
| 810 | 818 | .params = .{ .rounds_log = 5, .silently_truncate_password = false }, |
| 811 | 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 | 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 | 830 | try testing.expect(mem.startsWith(u8, s, crypt_format.prefix)); |
| 819 | 831 | try strVerify(s, "password", verify_options); |
| ... | ... | @@ -823,7 +835,11 @@ test "bcrypt crypt format" { |
| 823 | 835 | ); |
| 824 | 836 | |
| 825 | 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 | 844 | try testing.expect(mem.startsWith(u8, long_s, crypt_format.prefix)); |
| 829 | 845 | try strVerify(long_s, "password" ** 100, verify_options); |
| ... | ... | @@ -834,7 +850,11 @@ test "bcrypt crypt format" { |
| 834 | 850 | |
| 835 | 851 | hash_options.params.silently_truncate_password = true; |
| 836 | 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 | 858 | try strVerify(long_s, "password" ** 101, verify_options); |
| 839 | 859 | |
| 840 | 860 | try strVerify( |
| ... | ... | @@ -845,15 +865,20 @@ test "bcrypt crypt format" { |
| 845 | 865 | } |
| 846 | 866 | |
| 847 | 867 | test "bcrypt phc format" { |
| 848 | var hash_options = HashOptions{ | |
| 868 | const io = testing.io; | |
| 869 | var hash_options: HashOptions = .{ | |
| 849 | 870 | .params = .{ .rounds_log = 5, .silently_truncate_password = false }, |
| 850 | 871 | .encoding = .phc, |
| 851 | 872 | }; |
| 852 | var verify_options = VerifyOptions{ .silently_truncate_password = false }; | |
| 873 | var verify_options: VerifyOptions = .{ .silently_truncate_password = false }; | |
| 853 | 874 | const prefix = "$bcrypt$"; |
| 854 | 875 | |
| 855 | 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 | 883 | try testing.expect(mem.startsWith(u8, s, prefix)); |
| 859 | 884 | try strVerify(s, "password", verify_options); |
| ... | ... | @@ -863,7 +888,11 @@ test "bcrypt phc format" { |
| 863 | 888 | ); |
| 864 | 889 | |
| 865 | 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 | 897 | try testing.expect(mem.startsWith(u8, long_s, prefix)); |
| 869 | 898 | try strVerify(long_s, "password" ** 100, verify_options); |
| ... | ... | @@ -874,7 +903,11 @@ test "bcrypt phc format" { |
| 874 | 903 | |
| 875 | 904 | hash_options.params.silently_truncate_password = true; |
| 876 | 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 | 911 | try strVerify(long_s, "password" ** 101, verify_options); |
| 879 | 912 | |
| 880 | 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 | 323 | } |
| 324 | 324 | |
| 325 | 325 | /// Generate a new, random key pair. |
| 326 | pub fn generate() KeyPair { | |
| 326 | pub fn generate(io: std.Io) KeyPair { | |
| 327 | 327 | var random_seed: [seed_length]u8 = undefined; |
| 328 | 328 | while (true) { |
| 329 | crypto.random.bytes(&random_seed); | |
| 329 | io.random(&random_seed); | |
| 330 | 330 | return generateDeterministic(random_seed) catch { |
| 331 | 331 | @branchHint(.unlikely); |
| 332 | 332 | continue; |
| ... | ... | @@ -417,12 +417,13 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type { |
| 417 | 417 | test "Basic operations over EcdsaP384Sha384" { |
| 418 | 418 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 419 | 419 | |
| 420 | const io = testing.io; | |
| 420 | 421 | const Scheme = EcdsaP384Sha384; |
| 421 | const kp = Scheme.KeyPair.generate(); | |
| 422 | const kp = Scheme.KeyPair.generate(io); | |
| 422 | 423 | const msg = "test"; |
| 423 | 424 | |
| 424 | 425 | var noise: [Scheme.noise_length]u8 = undefined; |
| 425 | crypto.random.bytes(&noise); | |
| 426 | io.random(&noise); | |
| 426 | 427 | const sig = try kp.sign(msg, noise); |
| 427 | 428 | try sig.verify(msg, kp.public_key); |
| 428 | 429 | |
| ... | ... | @@ -433,12 +434,13 @@ test "Basic operations over EcdsaP384Sha384" { |
| 433 | 434 | test "Basic operations over Secp256k1" { |
| 434 | 435 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 435 | 436 | |
| 437 | const io = testing.io; | |
| 436 | 438 | const Scheme = EcdsaSecp256k1Sha256oSha256; |
| 437 | const kp = Scheme.KeyPair.generate(); | |
| 439 | const kp = Scheme.KeyPair.generate(io); | |
| 438 | 440 | const msg = "test"; |
| 439 | 441 | |
| 440 | 442 | var noise: [Scheme.noise_length]u8 = undefined; |
| 441 | crypto.random.bytes(&noise); | |
| 443 | io.random(&noise); | |
| 442 | 444 | const sig = try kp.sign(msg, noise); |
| 443 | 445 | try sig.verify(msg, kp.public_key); |
| 444 | 446 | |
| ... | ... | @@ -449,12 +451,13 @@ test "Basic operations over Secp256k1" { |
| 449 | 451 | test "Basic operations over EcdsaP384Sha256" { |
| 450 | 452 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 451 | 453 | |
| 454 | const io = testing.io; | |
| 452 | 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 | 457 | const msg = "test"; |
| 455 | 458 | |
| 456 | 459 | var noise: [Scheme.noise_length]u8 = undefined; |
| 457 | crypto.random.bytes(&noise); | |
| 460 | io.random(&noise); | |
| 458 | 461 | const sig = try kp.sign(msg, noise); |
| 459 | 462 | try sig.verify(msg, kp.public_key); |
| 460 | 463 | |
| ... | ... | @@ -502,8 +505,10 @@ test "Verifying a existing signature with EcdsaP384Sha256" { |
| 502 | 505 | test "Prehashed message operations" { |
| 503 | 506 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 504 | 507 | |
| 508 | const io = testing.io; | |
| 509 | ||
| 505 | 510 | const Scheme = EcdsaP256Sha256; |
| 506 | const kp = Scheme.KeyPair.generate(); | |
| 511 | const kp = Scheme.KeyPair.generate(io); | |
| 507 | 512 | const msg = "test message for prehashed signing"; |
| 508 | 513 | |
| 509 | 514 | const Hash = crypto.hash.sha2.Sha256; |
| ... | ... | @@ -518,7 +523,7 @@ test "Prehashed message operations" { |
| 518 | 523 | try testing.expectError(error.SignatureVerificationFailed, sig.verifyPrehashed(bad_hash, kp.public_key)); |
| 519 | 524 | |
| 520 | 525 | var noise: [Scheme.noise_length]u8 = undefined; |
| 521 | crypto.random.bytes(&noise); | |
| 526 | io.random(&noise); | |
| 522 | 527 | const sig_with_noise = try kp.signPrehashed(msg_hash, noise); |
| 523 | 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 | 1633 | test "Sec1 encoding/decoding" { |
| 1629 | 1634 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 1630 | 1635 | |
| 1636 | const io = testing.io; | |
| 1631 | 1637 | const Scheme = EcdsaP384Sha384; |
| 1632 | const kp = Scheme.KeyPair.generate(); | |
| 1638 | const kp = Scheme.KeyPair.generate(io); | |
| 1633 | 1639 | const pk = kp.public_key; |
| 1634 | 1640 | const pk_compressed_sec1 = pk.toCompressedSec1(); |
| 1635 | 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 | 174 | return .{ .bytes = buf.* }; |
| 175 | 175 | } |
| 176 | 176 | |
| 177 | /// Generates a shared secret and encapsulates it for the public key. | |
| 178 | /// If `seed` is `null`, uses random bytes from `std.crypto.random`. | |
| 179 | /// If `seed` is set, encapsulation is deterministic (for testing only). | |
| 180 | pub fn encaps(self: PublicKey, seed: ?[]const u8) !EncapsulatedSecret { | |
| 181 | const pq_nek = params.PqKem.PublicKey.encoded_length; | |
| 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 | ||
| 177 | /// Generates a shared secret, encapsulated for the public key, | |
| 178 | /// using random bytes. | |
| 179 | /// | |
| 180 | /// This is recommended over `encapsDeterministic`. | |
| 181 | pub fn encaps(pk: PublicKey, io: std.Io) !EncapsulatedSecret { | |
| 185 | 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| { | |
| 189 | if (r.len < 32) return error.InsufficientRandomness; | |
| 190 | seed_pq = r[0..32].*; | |
| 190 | /// Generates a shared secret, encapsulated for the public key, | |
| 191 | /// using the provided seed. | |
| 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 | 205 | if (t_randomness.len < params.Group.seed_length) { |
| 194 | // Provided randomness is shorter than seed_length, use it directly | |
| 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]); | |
| 206 | @memset(seed_t_expanded[t_randomness.len..], 0); | |
| 204 | 207 | } |
| 205 | 208 | } else { |
| 206 | crypto.random.bytes(&seed_pq); | |
| 207 | var seed_t: [32]u8 = undefined; | |
| 208 | crypto.random.bytes(&seed_t); | |
| 209 | seed_t_expanded = try expandRandomnessSeed(seed_t); | |
| 209 | // Full randomness provided | |
| 210 | @memcpy(&seed_t_expanded, t_randomness[0..params.Group.seed_length]); | |
| 210 | 211 | } |
| 211 | 212 | |
| 212 | const pq_encap = ek_pq.encaps(seed_pq); | |
| 213 | const sk_e = try params.Group.randomScalar(&seed_t_expanded); | |
| 213 | return encapsInner(pk, &seed_pq, &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 | 227 | const ct_t_point = try params.Group.mulBase(sk_e); |
| 215 | 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 | 293 | } |
| 281 | 294 | |
| 282 | 295 | /// Generates a new random key pair. |
| 283 | pub fn generate() !KeyPair { | |
| 296 | pub fn generate(io: std.Io) !KeyPair { | |
| 284 | 297 | var seed: [params.Nseed]u8 = undefined; |
| 285 | crypto.random.bytes(&seed); | |
| 298 | io.random(&seed); | |
| 286 | 299 | return generateDeterministic(seed); |
| 287 | 300 | } |
| 288 | 301 | }; |
| ... | ... | @@ -386,7 +399,7 @@ test "MLKEM768-X25519 basic round trip" { |
| 386 | 399 | var enc_seed: [64]u8 = undefined; |
| 387 | 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 | 403 | const ss_decap = try kp.secret_key.decaps(&encap_result.ciphertext); |
| 391 | 404 | |
| 392 | 405 | try testing.expectEqualSlices(u8, &encap_result.shared_secret, &ss_decap); |
| ... | ... | @@ -408,7 +421,7 @@ test "MLKEM768-X25519 test vector 0" { |
| 408 | 421 | const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed); |
| 409 | 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 | 425 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 413 | 426 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 414 | 427 | |
| ... | ... | @@ -432,7 +445,7 @@ test "MLKEM768-X25519 test vector 1" { |
| 432 | 445 | const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed); |
| 433 | 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 | 449 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 437 | 450 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 438 | 451 | |
| ... | ... | @@ -456,7 +469,7 @@ test "MLKEM768-X25519 test vector 2" { |
| 456 | 469 | const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed); |
| 457 | 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 | 473 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 461 | 474 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 462 | 475 | |
| ... | ... | @@ -480,7 +493,7 @@ test "MLKEM768-X25519 test vector 3" { |
| 480 | 493 | const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed); |
| 481 | 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 | 497 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 485 | 498 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 486 | 499 | |
| ... | ... | @@ -504,7 +517,7 @@ test "MLKEM768-X25519 test vector 4" { |
| 504 | 517 | const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed); |
| 505 | 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 | 521 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 509 | 522 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 510 | 523 | |
| ... | ... | @@ -528,7 +541,7 @@ test "MLKEM768-X25519 test vector 5" { |
| 528 | 541 | const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed); |
| 529 | 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 | 545 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 533 | 546 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 534 | 547 | |
| ... | ... | @@ -552,7 +565,7 @@ test "MLKEM768-X25519 test vector 6" { |
| 552 | 565 | const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed); |
| 553 | 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 | 569 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 557 | 570 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 558 | 571 | |
| ... | ... | @@ -576,7 +589,7 @@ test "MLKEM768-X25519 test vector 7" { |
| 576 | 589 | const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed); |
| 577 | 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 | 593 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 581 | 594 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 582 | 595 | |
| ... | ... | @@ -600,7 +613,7 @@ test "MLKEM768-X25519 test vector 8" { |
| 600 | 613 | const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed); |
| 601 | 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 | 617 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 605 | 618 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 606 | 619 | |
| ... | ... | @@ -624,7 +637,7 @@ test "MLKEM768-X25519 test vector 9" { |
| 624 | 637 | const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed); |
| 625 | 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 | 641 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 629 | 642 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 630 | 643 | |
| ... | ... | @@ -648,7 +661,7 @@ test "MLKEM768-P256 test vector 0" { |
| 648 | 661 | const kp = try MlKem768P256.KeyPair.generateDeterministic(seed); |
| 649 | 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 | 665 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 653 | 666 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 654 | 667 | |
| ... | ... | @@ -672,7 +685,7 @@ test "MLKEM768-P256 test vector 1" { |
| 672 | 685 | const kp = try MlKem768P256.KeyPair.generateDeterministic(seed); |
| 673 | 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 | 689 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 677 | 690 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 678 | 691 | |
| ... | ... | @@ -696,7 +709,7 @@ test "MLKEM768-P256 test vector 2" { |
| 696 | 709 | const kp = try MlKem768P256.KeyPair.generateDeterministic(seed); |
| 697 | 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 | 713 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 701 | 714 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 702 | 715 | |
| ... | ... | @@ -720,7 +733,7 @@ test "MLKEM768-P256 test vector 3" { |
| 720 | 733 | const kp = try MlKem768P256.KeyPair.generateDeterministic(seed); |
| 721 | 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 | 737 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 725 | 738 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 726 | 739 | |
| ... | ... | @@ -744,7 +757,7 @@ test "MLKEM768-P256 test vector 4" { |
| 744 | 757 | const kp = try MlKem768P256.KeyPair.generateDeterministic(seed); |
| 745 | 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 | 761 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 749 | 762 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 750 | 763 | |
| ... | ... | @@ -768,7 +781,7 @@ test "MLKEM768-P256 test vector 5" { |
| 768 | 781 | const kp = try MlKem768P256.KeyPair.generateDeterministic(seed); |
| 769 | 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 | 785 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 773 | 786 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 774 | 787 | |
| ... | ... | @@ -792,7 +805,7 @@ test "MLKEM768-P256 test vector 6" { |
| 792 | 805 | const kp = try MlKem768P256.KeyPair.generateDeterministic(seed); |
| 793 | 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 | 809 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 797 | 810 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 798 | 811 | |
| ... | ... | @@ -816,7 +829,7 @@ test "MLKEM768-P256 test vector 7" { |
| 816 | 829 | const kp = try MlKem768P256.KeyPair.generateDeterministic(seed); |
| 817 | 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 | 833 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 821 | 834 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 822 | 835 | |
| ... | ... | @@ -840,7 +853,7 @@ test "MLKEM768-P256 test vector 8" { |
| 840 | 853 | const kp = try MlKem768P256.KeyPair.generateDeterministic(seed); |
| 841 | 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 | 857 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 845 | 858 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 846 | 859 | |
| ... | ... | @@ -864,7 +877,7 @@ test "MLKEM768-P256 test vector 9" { |
| 864 | 877 | const kp = try MlKem768P256.KeyPair.generateDeterministic(seed); |
| 865 | 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 | 881 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 869 | 882 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 870 | 883 | |
| ... | ... | @@ -888,7 +901,7 @@ test "MLKEM1024-P384 test vector 0" { |
| 888 | 901 | const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed); |
| 889 | 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 | 905 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 893 | 906 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 894 | 907 | |
| ... | ... | @@ -912,7 +925,7 @@ test "MLKEM1024-P384 test vector 1" { |
| 912 | 925 | const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed); |
| 913 | 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 | 929 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 917 | 930 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 918 | 931 | |
| ... | ... | @@ -936,7 +949,7 @@ test "MLKEM1024-P384 test vector 2" { |
| 936 | 949 | const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed); |
| 937 | 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 | 953 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 941 | 954 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 942 | 955 | |
| ... | ... | @@ -960,7 +973,7 @@ test "MLKEM1024-P384 test vector 3" { |
| 960 | 973 | const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed); |
| 961 | 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 | 977 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 965 | 978 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 966 | 979 | |
| ... | ... | @@ -984,7 +997,7 @@ test "MLKEM1024-P384 test vector 4" { |
| 984 | 997 | const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed); |
| 985 | 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 | 1001 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 989 | 1002 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 990 | 1003 | |
| ... | ... | @@ -1008,7 +1021,7 @@ test "MLKEM1024-P384 test vector 5" { |
| 1008 | 1021 | const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed); |
| 1009 | 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 | 1025 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 1013 | 1026 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 1014 | 1027 | |
| ... | ... | @@ -1032,7 +1045,7 @@ test "MLKEM1024-P384 test vector 6" { |
| 1032 | 1045 | const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed); |
| 1033 | 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 | 1049 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 1037 | 1050 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 1038 | 1051 | |
| ... | ... | @@ -1056,7 +1069,7 @@ test "MLKEM1024-P384 test vector 7" { |
| 1056 | 1069 | const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed); |
| 1057 | 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 | 1073 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 1061 | 1074 | try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret); |
| 1062 | 1075 | |
| ... | ... | @@ -1080,7 +1093,7 @@ test "MLKEM1024-P384 test vector 8" { |
| 1080 | 1093 | const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed); |
| 1081 | 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 | 1097 | try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext); |
| 1085 | 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 | 2019 | secret_key: SecretKey, |
| 2020 | 2020 | |
| 2021 | 2021 | /// Generate a new random key pair. |
| 2022 | /// This uses the system's cryptographically secure random number generator. | |
| 2023 | /// | |
| 2024 | /// `crypto.random.bytes` must be supported by the target. | |
| 2025 | pub fn generate() KeyPair { | |
| 2022 | pub fn generate(io: std.Io) KeyPair { | |
| 2026 | 2023 | var seed: [Self.seed_length]u8 = undefined; |
| 2027 | crypto.random.bytes(&seed); | |
| 2024 | io.random(&seed); | |
| 2028 | 2025 | return generateDeterministic(seed) catch unreachable; |
| 2029 | 2026 | } |
| 2030 | 2027 | |
| ... | ... | @@ -3198,8 +3195,9 @@ test "ML-DSA-87 KAT test vector 0" { |
| 3198 | 3195 | } |
| 3199 | 3196 | |
| 3200 | 3197 | test "KeyPair API - generate and sign" { |
| 3198 | const io = std.testing.io; | |
| 3201 | 3199 | // Test the new KeyPair API with random generation |
| 3202 | const kp = MLDSA44.KeyPair.generate(); | |
| 3200 | const kp = MLDSA44.KeyPair.generate(io); | |
| 3203 | 3201 | const msg = "Test message for KeyPair API"; |
| 3204 | 3202 | |
| 3205 | 3203 | // Sign with deterministic mode (no noise) |
| ... | ... | @@ -3222,8 +3220,9 @@ test "KeyPair API - generateDeterministic" { |
| 3222 | 3220 | } |
| 3223 | 3221 | |
| 3224 | 3222 | test "KeyPair API - fromSecretKey" { |
| 3223 | const io = std.testing.io; | |
| 3225 | 3224 | // Generate a key pair |
| 3226 | const kp1 = MLDSA44.KeyPair.generate(); | |
| 3225 | const kp1 = MLDSA44.KeyPair.generate(io); | |
| 3227 | 3226 | |
| 3228 | 3227 | // Derive public key from secret key |
| 3229 | 3228 | const kp2 = try MLDSA44.KeyPair.fromSecretKey(kp1.secret_key); |
| ... | ... | @@ -3235,8 +3234,9 @@ test "KeyPair API - fromSecretKey" { |
| 3235 | 3234 | } |
| 3236 | 3235 | |
| 3237 | 3236 | test "Signature verification with noise" { |
| 3237 | const io = std.testing.io; | |
| 3238 | 3238 | // Test signing with randomness (hedged signatures) |
| 3239 | const kp = MLDSA65.KeyPair.generate(); | |
| 3239 | const kp = MLDSA65.KeyPair.generate(io); | |
| 3240 | 3240 | const msg = "Message to be signed with randomness"; |
| 3241 | 3241 | |
| 3242 | 3242 | // Create some noise |
| ... | ... | @@ -3250,8 +3250,9 @@ test "Signature verification with noise" { |
| 3250 | 3250 | } |
| 3251 | 3251 | |
| 3252 | 3252 | test "Signature verification failure" { |
| 3253 | const io = std.testing.io; | |
| 3253 | 3254 | // Test that invalid signatures are rejected |
| 3254 | const kp = MLDSA44.KeyPair.generate(); | |
| 3255 | const kp = MLDSA44.KeyPair.generate(io); | |
| 3255 | 3256 | const msg = "Original message"; |
| 3256 | 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 | 244 | /// Size of a serialized representation of the key, in bytes. |
| 245 | 245 | pub const encoded_length = InnerPk.encoded_length; |
| 246 | 246 | |
| 247 | /// Generates a shared secret, and encapsulates it for the public key. | |
| 248 | /// If `seed` is `null`, a random seed is used. This is recommended. | |
| 249 | /// If `seed` is set, encapsulation is deterministic. | |
| 250 | pub fn encaps(pk: PublicKey, seed_: ?[encaps_seed_length]u8) EncapsulatedSecret { | |
| 247 | /// Generates a shared secret, encapsulated for the public key, | |
| 248 | /// using random bytes. | |
| 249 | /// | |
| 250 | /// This is recommended over `encapsDeterministic`. | |
| 251 | pub fn encaps(pk: PublicKey, io: std.Io) EncapsulatedSecret { | |
| 251 | 252 | var m: [inner_plaintext_length]u8 = undefined; |
| 253 | io.random(&m); | |
| 254 | return encapsInner(pk, &m); | |
| 255 | } | |
| 252 | 256 | |
| 253 | if (seed_) |seed| { | |
| 254 | if (p.ml_kem) { | |
| 255 | @memcpy(&m, &seed); | |
| 256 | } else { | |
| 257 | // m = H(seed) | |
| 258 | sha3.Sha3_256.hash(&seed, &m, .{}); | |
| 259 | } | |
| 257 | /// Generates a shared secret, encapsulated for the public key, | |
| 258 | /// using the provided seed. | |
| 259 | /// | |
| 260 | /// Calling `encaps` instead is recommended. | |
| 261 | pub fn encapsDeterministic(pk: PublicKey, seed: *const [encaps_seed_length]u8) EncapsulatedSecret { | |
| 262 | var m: [inner_plaintext_length]u8 = undefined; | |
| 263 | if (p.ml_kem) { | |
| 264 | @memcpy(&m, seed); | |
| 260 | 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 | 273 | // (K', r) = G(m ‖ H(pk)) |
| 265 | 274 | var kr: [inner_plaintext_length + h_length]u8 = undefined; |
| 266 | 275 | var g = sha3.Sha3_512.init(.{}); |
| 267 | g.update(&m); | |
| 276 | g.update(m); | |
| 268 | 277 | g.update(&pk.hpk); |
| 269 | 278 | g.final(&kr); |
| 270 | 279 | |
| 271 | 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 | 283 | if (p.ml_kem) { |
| 275 | 284 | return EncapsulatedSecret{ |
| ... | ... | @@ -398,10 +407,10 @@ fn Kyber(comptime p: Params) type { |
| 398 | 407 | } |
| 399 | 408 | |
| 400 | 409 | /// Generate a new, random key pair. |
| 401 | pub fn generate() KeyPair { | |
| 410 | pub fn generate(io: std.Io) KeyPair { | |
| 402 | 411 | var random_seed: [seed_length]u8 = undefined; |
| 403 | 412 | while (true) { |
| 404 | crypto.random.bytes(&random_seed); | |
| 413 | io.random(&random_seed); | |
| 405 | 414 | return generateDeterministic(random_seed) catch { |
| 406 | 415 | @branchHint(.unlikely); |
| 407 | 416 | continue; |
| ... | ... | @@ -1634,15 +1643,15 @@ test "Test happy flow" { |
| 1634 | 1643 | } |
| 1635 | 1644 | inline for (modes) |mode| { |
| 1636 | 1645 | for (0..10) |i| { |
| 1637 | seed[0] = @as(u8, @intCast(i)); | |
| 1646 | seed[0] = @intCast(i); | |
| 1638 | 1647 | const kp = try mode.KeyPair.generateDeterministic(seed); |
| 1639 | 1648 | const sk = try mode.SecretKey.fromBytes(&kp.secret_key.toBytes()); |
| 1640 | 1649 | try testing.expectEqual(sk, kp.secret_key); |
| 1641 | 1650 | const pk = try mode.PublicKey.fromBytes(&kp.public_key.toBytes()); |
| 1642 | 1651 | try testing.expectEqual(pk, kp.public_key); |
| 1643 | 1652 | for (0..10) |j| { |
| 1644 | seed[1] = @as(u8, @intCast(j)); | |
| 1645 | const e = pk.encaps(seed[0..32].*); | |
| 1653 | seed[1] = @intCast(j); | |
| 1654 | const e = pk.encapsDeterministic(seed[0..32]); | |
| 1646 | 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 | 1704 | g2.fill(kseed[32..64]); |
| 1696 | 1705 | g2.fill(&eseed); |
| 1697 | 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 | 1708 | const ss2 = try kp.secret_key.decaps(&e.ciphertext); |
| 1700 | 1709 | try testing.expectEqual(ss2, e.shared_secret); |
| 1701 | 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 | 122 | } |
| 123 | 123 | |
| 124 | 124 | /// Return a random point. |
| 125 | pub fn random() P256 { | |
| 126 | const n = scalar.random(.little); | |
| 125 | pub fn random(io: std.Io) P256 { | |
| 126 | const n = scalar.random(io, .little); | |
| 127 | 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 | 68 | } |
| 69 | 69 | |
| 70 | 70 | /// Return a random scalar |
| 71 | pub fn random(endian: std.builtin.Endian) CompressedScalar { | |
| 72 | return Scalar.random().toBytes(endian); | |
| 71 | pub fn random(io: std.Io, endian: std.builtin.Endian) CompressedScalar { | |
| 72 | return Scalar.random(io).toBytes(endian); | |
| 73 | 73 | } |
| 74 | 74 | |
| 75 | 75 | /// A scalar in unpacked representation. |
| ... | ... | @@ -170,10 +170,10 @@ pub const Scalar = struct { |
| 170 | 170 | } |
| 171 | 171 | |
| 172 | 172 | /// Return a random scalar < L. |
| 173 | pub fn random() Scalar { | |
| 173 | pub fn random(io: std.Io) Scalar { | |
| 174 | 174 | var s: [48]u8 = undefined; |
| 175 | 175 | while (true) { |
| 176 | crypto.random.bytes(&s); | |
| 176 | io.random(&s); | |
| 177 | 177 | const n = Scalar.fromBytes48(s, .little); |
| 178 | 178 | if (!n.isZero()) { |
| 179 | 179 | return n; |
lib/std/crypto/pcurves/p384.zig+2-2| ... | ... | @@ -122,8 +122,8 @@ pub const P384 = struct { |
| 122 | 122 | } |
| 123 | 123 | |
| 124 | 124 | /// Return a random point. |
| 125 | pub fn random() P384 { | |
| 126 | const n = scalar.random(.little); | |
| 125 | pub fn random(io: std.Io) P384 { | |
| 126 | const n = scalar.random(io, .little); | |
| 127 | 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 | 63 | } |
| 64 | 64 | |
| 65 | 65 | /// Return a random scalar |
| 66 | pub fn random(endian: std.builtin.Endian) CompressedScalar { | |
| 67 | return Scalar.random().toBytes(endian); | |
| 66 | pub fn random(io: std.Io, endian: std.builtin.Endian) CompressedScalar { | |
| 67 | return Scalar.random(io).toBytes(endian); | |
| 68 | 68 | } |
| 69 | 69 | |
| 70 | 70 | /// A scalar in unpacked representation. |
| ... | ... | @@ -159,10 +159,10 @@ pub const Scalar = struct { |
| 159 | 159 | } |
| 160 | 160 | |
| 161 | 161 | /// Return a random scalar < L. |
| 162 | pub fn random() Scalar { | |
| 162 | pub fn random(io: std.Io) Scalar { | |
| 163 | 163 | var s: [64]u8 = undefined; |
| 164 | 164 | while (true) { |
| 165 | crypto.random.bytes(&s); | |
| 165 | io.random(&s); | |
| 166 | 166 | const n = Scalar.fromBytes64(s, .little); |
| 167 | 167 | if (!n.isZero()) { |
| 168 | 168 | return n; |
lib/std/crypto/pcurves/secp256k1.zig+2-2| ... | ... | @@ -175,8 +175,8 @@ pub const Secp256k1 = struct { |
| 175 | 175 | } |
| 176 | 176 | |
| 177 | 177 | /// Return a random point. |
| 178 | pub fn random() Secp256k1 { | |
| 179 | const n = scalar.random(.little); | |
| 178 | pub fn random(io: std.Io) Secp256k1 { | |
| 179 | const n = scalar.random(io, .little); | |
| 180 | 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 | 68 | } |
| 69 | 69 | |
| 70 | 70 | /// Return a random scalar |
| 71 | pub fn random(endian: std.builtin.Endian) CompressedScalar { | |
| 72 | return Scalar.random().toBytes(endian); | |
| 71 | pub fn random(io: std.Io, endian: std.builtin.Endian) CompressedScalar { | |
| 72 | return Scalar.random(io).toBytes(endian); | |
| 73 | 73 | } |
| 74 | 74 | |
| 75 | 75 | /// A scalar in unpacked representation. |
| ... | ... | @@ -170,10 +170,10 @@ pub const Scalar = struct { |
| 170 | 170 | } |
| 171 | 171 | |
| 172 | 172 | /// Return a random scalar < L. |
| 173 | pub fn random() Scalar { | |
| 173 | pub fn random(io: std.Io) Scalar { | |
| 174 | 174 | var s: [48]u8 = undefined; |
| 175 | 175 | while (true) { |
| 176 | crypto.random.bytes(&s); | |
| 176 | io.random(&s); | |
| 177 | 177 | const n = Scalar.fromBytes48(s, .little); |
| 178 | 178 | if (!n.isZero()) { |
| 179 | 179 | return n; |
lib/std/crypto/pcurves/tests/p256.zig+11-6| ... | ... | @@ -5,8 +5,9 @@ const testing = std.testing; |
| 5 | 5 | const P256 = @import("../p256.zig").P256; |
| 6 | 6 | |
| 7 | 7 | test "p256 ECDH key exchange" { |
| 8 | const dha = P256.scalar.random(.little); | |
| 9 | const dhb = P256.scalar.random(.little); | |
| 8 | const io = testing.io; | |
| 9 | const dha = P256.scalar.random(io, .little); | |
| 10 | const dhb = P256.scalar.random(io, .little); | |
| 10 | 11 | const dhA = try P256.basePoint.mul(dha, .little); |
| 11 | 12 | const dhB = try P256.basePoint.mul(dhb, .little); |
| 12 | 13 | const shareda = try dhA.mul(dhb, .little); |
| ... | ... | @@ -66,28 +67,32 @@ test "p256 test vectors - doubling" { |
| 66 | 67 | } |
| 67 | 68 | |
| 68 | 69 | test "p256 compressed sec1 encoding/decoding" { |
| 69 | const p = P256.random(); | |
| 70 | const io = testing.io; | |
| 71 | const p = P256.random(io); | |
| 70 | 72 | const s = p.toCompressedSec1(); |
| 71 | 73 | const q = try P256.fromSec1(&s); |
| 72 | 74 | try testing.expect(p.equivalent(q)); |
| 73 | 75 | } |
| 74 | 76 | |
| 75 | 77 | test "p256 uncompressed sec1 encoding/decoding" { |
| 76 | const p = P256.random(); | |
| 78 | const io = testing.io; | |
| 79 | const p = P256.random(io); | |
| 77 | 80 | const s = p.toUncompressedSec1(); |
| 78 | 81 | const q = try P256.fromSec1(&s); |
| 79 | 82 | try testing.expect(p.equivalent(q)); |
| 80 | 83 | } |
| 81 | 84 | |
| 82 | 85 | test "p256 public key is the neutral element" { |
| 86 | const io = testing.io; | |
| 83 | 87 | const n = P256.scalar.Scalar.zero.toBytes(.little); |
| 84 | const p = P256.random(); | |
| 88 | const p = P256.random(io); | |
| 85 | 89 | try testing.expectError(error.IdentityElement, p.mul(n, .little)); |
| 86 | 90 | } |
| 87 | 91 | |
| 88 | 92 | test "p256 public key is the neutral element (public verification)" { |
| 93 | const io = testing.io; | |
| 89 | 94 | const n = P256.scalar.Scalar.zero.toBytes(.little); |
| 90 | const p = P256.random(); | |
| 95 | const p = P256.random(io); | |
| 91 | 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 | 5 | const P384 = @import("../p384.zig").P384; |
| 6 | 6 | |
| 7 | 7 | test "p384 ECDH key exchange" { |
| 8 | const dha = P384.scalar.random(.little); | |
| 9 | const dhb = P384.scalar.random(.little); | |
| 8 | const io = testing.io; | |
| 9 | const dha = P384.scalar.random(io, .little); | |
| 10 | const dhb = P384.scalar.random(io, .little); | |
| 10 | 11 | const dhA = try P384.basePoint.mul(dha, .little); |
| 11 | 12 | const dhB = try P384.basePoint.mul(dhb, .little); |
| 12 | 13 | const shareda = try dhA.mul(dhb, .little); |
| ... | ... | @@ -67,7 +68,8 @@ test "p384 test vectors - doubling" { |
| 67 | 68 | } |
| 68 | 69 | |
| 69 | 70 | test "p384 compressed sec1 encoding/decoding" { |
| 70 | const p = P384.random(); | |
| 71 | const io = testing.io; | |
| 72 | const p = P384.random(io); | |
| 71 | 73 | const s0 = p.toUncompressedSec1(); |
| 72 | 74 | const s = p.toCompressedSec1(); |
| 73 | 75 | try testing.expectEqualSlices(u8, s0[1..49], s[1..49]); |
| ... | ... | @@ -76,21 +78,24 @@ test "p384 compressed sec1 encoding/decoding" { |
| 76 | 78 | } |
| 77 | 79 | |
| 78 | 80 | test "p384 uncompressed sec1 encoding/decoding" { |
| 79 | const p = P384.random(); | |
| 81 | const io = testing.io; | |
| 82 | const p = P384.random(io); | |
| 80 | 83 | const s = p.toUncompressedSec1(); |
| 81 | 84 | const q = try P384.fromSec1(&s); |
| 82 | 85 | try testing.expect(p.equivalent(q)); |
| 83 | 86 | } |
| 84 | 87 | |
| 85 | 88 | test "p384 public key is the neutral element" { |
| 89 | const io = testing.io; | |
| 86 | 90 | const n = P384.scalar.Scalar.zero.toBytes(.little); |
| 87 | const p = P384.random(); | |
| 91 | const p = P384.random(io); | |
| 88 | 92 | try testing.expectError(error.IdentityElement, p.mul(n, .little)); |
| 89 | 93 | } |
| 90 | 94 | |
| 91 | 95 | test "p384 public key is the neutral element (public verification)" { |
| 96 | const io = testing.io; | |
| 92 | 97 | const n = P384.scalar.Scalar.zero.toBytes(.little); |
| 93 | const p = P384.random(); | |
| 98 | const p = P384.random(io); | |
| 94 | 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 | 5 | const Secp256k1 = @import("../secp256k1.zig").Secp256k1; |
| 6 | 6 | |
| 7 | 7 | test "secp256k1 ECDH key exchange" { |
| 8 | const dha = Secp256k1.scalar.random(.little); | |
| 9 | const dhb = Secp256k1.scalar.random(.little); | |
| 8 | const io = testing.io; | |
| 9 | const dha = Secp256k1.scalar.random(io, .little); | |
| 10 | const dhb = Secp256k1.scalar.random(io, .little); | |
| 10 | 11 | const dhA = try Secp256k1.basePoint.mul(dha, .little); |
| 11 | 12 | const dhB = try Secp256k1.basePoint.mul(dhb, .little); |
| 12 | 13 | const shareda = try dhA.mul(dhb, .little); |
| ... | ... | @@ -15,8 +16,9 @@ test "secp256k1 ECDH key exchange" { |
| 15 | 16 | } |
| 16 | 17 | |
| 17 | 18 | test "secp256k1 ECDH key exchange including public multiplication" { |
| 18 | const dha = Secp256k1.scalar.random(.little); | |
| 19 | const dhb = Secp256k1.scalar.random(.little); | |
| 19 | const io = testing.io; | |
| 20 | const dha = Secp256k1.scalar.random(io, .little); | |
| 21 | const dhb = Secp256k1.scalar.random(io, .little); | |
| 20 | 22 | const dhA = try Secp256k1.basePoint.mul(dha, .little); |
| 21 | 23 | const dhB = try Secp256k1.basePoint.mulPublic(dhb, .little); |
| 22 | 24 | const shareda = try dhA.mul(dhb, .little); |
| ... | ... | @@ -77,28 +79,32 @@ test "secp256k1 test vectors - doubling" { |
| 77 | 79 | } |
| 78 | 80 | |
| 79 | 81 | test "secp256k1 compressed sec1 encoding/decoding" { |
| 80 | const p = Secp256k1.random(); | |
| 82 | const io = testing.io; | |
| 83 | const p = Secp256k1.random(io); | |
| 81 | 84 | const s = p.toCompressedSec1(); |
| 82 | 85 | const q = try Secp256k1.fromSec1(&s); |
| 83 | 86 | try testing.expect(p.equivalent(q)); |
| 84 | 87 | } |
| 85 | 88 | |
| 86 | 89 | test "secp256k1 uncompressed sec1 encoding/decoding" { |
| 87 | const p = Secp256k1.random(); | |
| 90 | const io = testing.io; | |
| 91 | const p = Secp256k1.random(io); | |
| 88 | 92 | const s = p.toUncompressedSec1(); |
| 89 | 93 | const q = try Secp256k1.fromSec1(&s); |
| 90 | 94 | try testing.expect(p.equivalent(q)); |
| 91 | 95 | } |
| 92 | 96 | |
| 93 | 97 | test "secp256k1 public key is the neutral element" { |
| 98 | const io = testing.io; | |
| 94 | 99 | const n = Secp256k1.scalar.Scalar.zero.toBytes(.little); |
| 95 | const p = Secp256k1.random(); | |
| 100 | const p = Secp256k1.random(io); | |
| 96 | 101 | try testing.expectError(error.IdentityElement, p.mul(n, .little)); |
| 97 | 102 | } |
| 98 | 103 | |
| 99 | 104 | test "secp256k1 public key is the neutral element (public verification)" { |
| 105 | const io = testing.io; | |
| 100 | 106 | const n = Secp256k1.scalar.Scalar.zero.toBytes(.little); |
| 101 | const p = Secp256k1.random(); | |
| 107 | const p = Secp256k1.random(io); | |
| 102 | 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 | 533 | |
| 534 | 534 | /// Encrypt a message `m` for a recipient whose public key is `public_key`. |
| 535 | 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 | 537 | debug.assert(c.len == m.len + seal_length); |
| 538 | var ekp = KeyPair.generate(); | |
| 538 | var ekp = KeyPair.generate(io); | |
| 539 | 539 | const nonce = createNonce(ekp.public_key, public_key); |
| 540 | 540 | c[0..public_length].* = ekp.public_key; |
| 541 | 541 | try Box.seal(c[Box.public_length..], m, nonce, public_key, ekp.secret_key); |
| ... | ... | @@ -573,29 +573,31 @@ test "(x)salsa20" { |
| 573 | 573 | } |
| 574 | 574 | |
| 575 | 575 | test "xsalsa20poly1305" { |
| 576 | const io = std.testing.io; | |
| 576 | 577 | var msg: [100]u8 = undefined; |
| 577 | 578 | var msg2: [msg.len]u8 = undefined; |
| 578 | 579 | var c: [msg.len]u8 = undefined; |
| 579 | 580 | var key: [XSalsa20Poly1305.key_length]u8 = undefined; |
| 580 | 581 | var nonce: [XSalsa20Poly1305.nonce_length]u8 = undefined; |
| 581 | 582 | var tag: [XSalsa20Poly1305.tag_length]u8 = undefined; |
| 582 | crypto.random.bytes(&msg); | |
| 583 | crypto.random.bytes(&key); | |
| 584 | crypto.random.bytes(&nonce); | |
| 583 | io.random(&msg); | |
| 584 | io.random(&key); | |
| 585 | io.random(&nonce); | |
| 585 | 586 | |
| 586 | 587 | XSalsa20Poly1305.encrypt(c[0..], &tag, msg[0..], "ad", nonce, key); |
| 587 | 588 | try XSalsa20Poly1305.decrypt(msg2[0..], c[0..], tag, "ad", nonce, key); |
| 588 | 589 | } |
| 589 | 590 | |
| 590 | 591 | test "xsalsa20poly1305 secretbox" { |
| 592 | const io = std.testing.io; | |
| 591 | 593 | var msg: [100]u8 = undefined; |
| 592 | 594 | var msg2: [msg.len]u8 = undefined; |
| 593 | 595 | var key: [XSalsa20Poly1305.key_length]u8 = undefined; |
| 594 | 596 | var nonce: [Box.nonce_length]u8 = undefined; |
| 595 | 597 | var boxed: [msg.len + Box.tag_length]u8 = undefined; |
| 596 | crypto.random.bytes(&msg); | |
| 597 | crypto.random.bytes(&key); | |
| 598 | crypto.random.bytes(&nonce); | |
| 598 | io.random(&msg); | |
| 599 | io.random(&key); | |
| 600 | io.random(&nonce); | |
| 599 | 601 | |
| 600 | 602 | SecretBox.seal(boxed[0..], msg[0..], nonce, key); |
| 601 | 603 | try SecretBox.open(msg2[0..], boxed[0..], nonce, key); |
| ... | ... | @@ -604,15 +606,16 @@ test "xsalsa20poly1305 secretbox" { |
| 604 | 606 | test "xsalsa20poly1305 box" { |
| 605 | 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 | 610 | var msg: [100]u8 = undefined; |
| 608 | 611 | var msg2: [msg.len]u8 = undefined; |
| 609 | 612 | var nonce: [Box.nonce_length]u8 = undefined; |
| 610 | 613 | var boxed: [msg.len + Box.tag_length]u8 = undefined; |
| 611 | crypto.random.bytes(&msg); | |
| 612 | crypto.random.bytes(&nonce); | |
| 614 | io.random(&msg); | |
| 615 | io.random(&nonce); | |
| 613 | 616 | |
| 614 | const kp1 = Box.KeyPair.generate(); | |
| 615 | const kp2 = Box.KeyPair.generate(); | |
| 617 | const kp1 = Box.KeyPair.generate(io); | |
| 618 | const kp2 = Box.KeyPair.generate(io); | |
| 616 | 619 | try Box.seal(boxed[0..], msg[0..], nonce, kp1.public_key, kp2.secret_key); |
| 617 | 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 | 623 | test "xsalsa20poly1305 sealedbox" { |
| 621 | 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 | 627 | var msg: [100]u8 = undefined; |
| 624 | 628 | var msg2: [msg.len]u8 = undefined; |
| 625 | 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(); | |
| 629 | try SealedBox.seal(boxed[0..], msg[0..], kp.public_key); | |
| 632 | const kp = Box.KeyPair.generate(io); | |
| 633 | try SealedBox.seal(io, boxed[0..], msg[0..], kp.public_key); | |
| 630 | 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 | 20 | |
| 21 | 21 | const max_size = math.maxInt(usize); |
| 22 | 22 | const max_int = max_size >> 1; |
| 23 | const default_salt_len = 32; | |
| 23 | pub const default_salt_len = 32; | |
| 24 | 24 | const default_hash_len = 32; |
| 25 | 25 | const max_salt_len = 64; |
| 26 | 26 | const max_hash_len = 64; |
| ... | ... | @@ -417,10 +417,9 @@ const PhcFormatHasher = struct { |
| 417 | 417 | password: []const u8, |
| 418 | 418 | params: Params, |
| 419 | 419 | buf: []u8, |
| 420 | /// Filled with cryptographically secure entropy. | |
| 421 | salt: []const u8, | |
| 420 | 422 | ) HasherError![]const u8 { |
| 421 | var salt: [default_salt_len]u8 = undefined; | |
| 422 | crypto.random.bytes(&salt); | |
| 423 | ||
| 424 | 423 | var hash: [default_hash_len]u8 = undefined; |
| 425 | 424 | try kdf(allocator, &hash, password, &salt, params); |
| 426 | 425 | |
| ... | ... | @@ -466,9 +465,9 @@ const CryptFormatHasher = struct { |
| 466 | 465 | password: []const u8, |
| 467 | 466 | params: Params, |
| 468 | 467 | buf: []u8, |
| 468 | /// Filled with cryptographically secure entropy. | |
| 469 | salt_bin: []const u8, | |
| 469 | 470 | ) HasherError![]const u8 { |
| 470 | var salt_bin: [default_salt_len]u8 = undefined; | |
| 471 | crypto.random.bytes(&salt_bin); | |
| 472 | 471 | const salt = crypt_format.saltFromBin(salt_bin.len, salt_bin); |
| 473 | 472 | |
| 474 | 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 | 180 | } |
| 181 | 181 | |
| 182 | 182 | test eql { |
| 183 | const random = std.crypto.random; | |
| 183 | const io = std.testing.io; | |
| 184 | 184 | const expect = std.testing.expect; |
| 185 | 185 | var a: [100]u8 = undefined; |
| 186 | 186 | var b: [100]u8 = undefined; |
| 187 | random.bytes(a[0..]); | |
| 188 | random.bytes(b[0..]); | |
| 187 | io.random(&a); | |
| 188 | io.random(&b); | |
| 189 | 189 | try expect(!eql([100]u8, a, b)); |
| 190 | 190 | a = b; |
| 191 | 191 | try expect(eql([100]u8, a, b)); |
| 192 | 192 | } |
| 193 | 193 | |
| 194 | 194 | test "eql (vectors)" { |
| 195 | const random = std.crypto.random; | |
| 195 | const io = std.testing.io; | |
| 196 | 196 | const expect = std.testing.expect; |
| 197 | 197 | var a: [100]u8 = undefined; |
| 198 | 198 | var b: [100]u8 = undefined; |
| 199 | random.bytes(a[0..]); | |
| 200 | random.bytes(b[0..]); | |
| 199 | io.random(&a); | |
| 200 | io.random(&b); | |
| 201 | 201 | const v1: @Vector(100, u8) = a; |
| 202 | 202 | const v2: @Vector(100, u8) = b; |
| 203 | 203 | try expect(!eql(@Vector(100, u8), v1, v2)); |
| ... | ... | @@ -220,9 +220,10 @@ test compare { |
| 220 | 220 | } |
| 221 | 221 | |
| 222 | 222 | test "add and sub" { |
| 223 | const io = std.testing.io; | |
| 224 | ||
| 223 | 225 | const expectEqual = std.testing.expectEqual; |
| 224 | 226 | const expectEqualSlices = std.testing.expectEqualSlices; |
| 225 | const random = std.crypto.random; | |
| 226 | 227 | const len = 32; |
| 227 | 228 | var a: [len]u8 = undefined; |
| 228 | 229 | var b: [len]u8 = undefined; |
| ... | ... | @@ -230,8 +231,8 @@ test "add and sub" { |
| 230 | 231 | const zero = [_]u8{0} ** len; |
| 231 | 232 | var iterations: usize = 100; |
| 232 | 233 | while (iterations != 0) : (iterations -= 1) { |
| 233 | random.bytes(&a); | |
| 234 | random.bytes(&b); | |
| 234 | io.random(&a); | |
| 235 | io.random(&b); | |
| 235 | 236 | const endian = if (iterations % 2 == 0) Endian.big else Endian.little; |
| 236 | 237 | _ = sub(u8, &a, &b, &c, endian); // a-b |
| 237 | 238 | _ = add(u8, &c, &b, &c, endian); // (a-b)+b |
| ... | ... | @@ -243,11 +244,11 @@ test "add and sub" { |
| 243 | 244 | } |
| 244 | 245 | |
| 245 | 246 | test classify { |
| 246 | const random = std.crypto.random; | |
| 247 | const io = std.testing.io; | |
| 247 | 248 | const expect = std.testing.expect; |
| 248 | 249 | |
| 249 | 250 | var secret: [32]u8 = undefined; |
| 250 | random.bytes(&secret); | |
| 251 | io.random(&secret); | |
| 251 | 252 | |
| 252 | 253 | // Input of the hash function is marked as secret |
| 253 | 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 | 109 | read_buffer: []u8, |
| 110 | 110 | /// Cryptographically secure random bytes. The pointer is not captured; data is only |
| 111 | 111 | /// read during `init`. |
| 112 | entropy: *const [176]u8, | |
| 112 | entropy: *const [entropy_len]u8, | |
| 113 | 113 | /// Current time according to the wall clock / calendar, in seconds. |
| 114 | 114 | realtime_now_seconds: i64, |
| 115 | 115 | |
| ... | ... | @@ -130,6 +130,8 @@ pub const Options = struct { |
| 130 | 130 | allow_truncation_attacks: bool = false, |
| 131 | 131 | /// Populated when `error.TlsAlert` is returned from `init`. |
| 132 | 132 | alert: ?*tls.Alert = null, |
| 133 | ||
| 134 | pub const entropy_len = 240; | |
| 133 | 135 | }; |
| 134 | 136 | |
| 135 | 137 | const InitError = error{ |
| ... | ... | @@ -200,7 +202,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 200 | 202 | var server_hello_rand: [32]u8 = undefined; |
| 201 | 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 | 206 | // Only possible to happen if the seed is all zeroes. |
| 205 | 207 | error.IdentityElement => return error.InsufficientEntropy, |
| 206 | 208 | }; |
| ... | ... | @@ -1330,12 +1332,12 @@ const KeyShare = struct { |
| 1330 | 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 | 1336 | return .{ |
| 1335 | .ml_kem768_kp = .generate(), | |
| 1336 | .secp256r1_kp = try .generateDeterministic(seed[0..32].*), | |
| 1337 | .secp384r1_kp = try .generateDeterministic(seed[32..80].*), | |
| 1338 | .x25519_kp = try .generateDeterministic(seed[80..112].*), | |
| 1337 | .ml_kem768_kp = try .generateDeterministic(seed[0..64].*), | |
| 1338 | .secp256r1_kp = try .generateDeterministic(seed[64..96].*), | |
| 1339 | .secp384r1_kp = try .generateDeterministic(seed[96..144].*), | |
| 1340 | .x25519_kp = try .generateDeterministic(seed[144..176].*), | |
| 1339 | 1341 | .sk_buf = undefined, |
| 1340 | 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 | 1022 | file.close(io); |
| 1023 | 1023 | target_dir.close(io); |
| 1024 | 1024 | |
| 1025 | // Rename should fail with PathAlreadyExists if target_dir is non-empty | |
| 1026 | try expectError(error.PathAlreadyExists, ctx.dir.rename(test_dir_path, ctx.dir, target_dir_path, io)); | |
| 1025 | try expectError(error.DirNotEmpty, ctx.dir.rename(test_dir_path, ctx.dir, target_dir_path, io)); | |
| 1027 | 1026 | |
| 1028 | 1027 | // Ensure the directory was not renamed |
| 1029 | 1028 | var dir = try ctx.dir.openDir(io, test_dir_path, .{}); |
| ... | ... | @@ -1651,6 +1650,21 @@ test "AtomicFile" { |
| 1651 | 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 | 1669 | var af = try ctx.dir.createFileAtomic(io, test_out_file, .{ .replace = true }); |
| 1656 | 1670 | defer af.deinit(io); |
| ... | ... | @@ -1761,7 +1775,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" { |
| 1761 | 1775 | const io = testing.io; |
| 1762 | 1776 | |
| 1763 | 1777 | var random_bytes: [12]u8 = undefined; |
| 1764 | std.crypto.random.bytes(&random_bytes); | |
| 1778 | io.random(&random_bytes); | |
| 1765 | 1779 | |
| 1766 | 1780 | var random_b64: [std.fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined; |
| 1767 | 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 | 321 | assert(base.ptr + alloc_len == socket_read_buffer.ptr + socket_read_buffer.len); |
| 322 | 322 | @memcpy(host_buffer, remote_host.bytes); |
| 323 | 323 | const tls: *Tls = @ptrCast(base); |
| 324 | var random_buffer: [176]u8 = undefined; | |
| 325 | std.crypto.random.bytes(&random_buffer); | |
| 324 | var random_buffer: [std.crypto.tls.Client.Options.entropy_len]u8 = undefined; | |
| 325 | io.random(&random_buffer); | |
| 326 | 326 | tls.* = .{ |
| 327 | 327 | .connection = .{ |
| 328 | 328 | .client = client, |
lib/std/os/linux.zig+26-4| ... | ... | @@ -501,6 +501,15 @@ pub const O = switch (native_arch) { |
| 501 | 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 | 513 | /// Set by startup code, used by `getauxval`. |
| 505 | 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 | 1355 | if (@hasField(SYS, "rename")) { |
| 1347 | 1356 | return syscall2(.rename, @intFromPtr(old), @intFromPtr(new)); |
| 1348 | 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 | 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 | 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 | 1399 | return syscall5( |
| 1378 | 1400 | .renameat2, |
| 1379 | 1401 | @as(usize, @bitCast(@as(isize, oldfd))), |
| 1380 | 1402 | @intFromPtr(oldpath), |
| 1381 | 1403 | @as(usize, @bitCast(@as(isize, newfd))), |
| 1382 | 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 | 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 | |
| 535 | /// the thread-local variables of the `std.crypto.random` implementation. I'm not sure why it ends up | |
| 536 | /// being so much; the struct itself is only 64 bytes. I think it has to do with being page-aligned | |
| 537 | /// and LLVM or LLD is not smart enough to lay out the TLS data in a space-conserving way. Anyway, I | |
| 538 | /// think it's fine because it's less than 3 pages of memory, and putting it in the ELF like this is | |
| 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; | |
| 534 | /// The main motivation for the size chosen here is to be larger than total | |
| 535 | /// amount of thread-local variables for most programs. Putting this allocation | |
| 536 | /// in the ELF like this is equivalent to moving the `mmap` call below into the | |
| 537 | /// kernel, avoiding syscall overhead. | |
| 538 | var main_thread_area_buffer: [0x1000]u8 align(page_size_min) = undefined; | |
| 541 | 539 | |
| 542 | 540 | /// Computes the layout of the static TLS area, allocates the area, initializes all of its fields, |
| 543 | 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 | 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 | 2650 | pub const WaitForSingleObjectError = error{ |
| 2707 | 2651 | WaitAbandoned, |
| 2708 | 2652 | WaitTimeOut, |
| ... | ... | @@ -3250,7 +3194,7 @@ pub const RenameError = error{ |
| 3250 | 3194 | NetworkNotFound, |
| 3251 | 3195 | AntivirusInterference, |
| 3252 | 3196 | BadPathName, |
| 3253 | RenameAcrossMountPoints, | |
| 3197 | CrossDevice, | |
| 3254 | 3198 | } || UnexpectedError; |
| 3255 | 3199 | |
| 3256 | 3200 | pub fn RenameFile( |
| ... | ... | @@ -3351,7 +3295,7 @@ pub fn RenameFile( |
| 3351 | 3295 | .ACCESS_DENIED => return error.AccessDenied, |
| 3352 | 3296 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, |
| 3353 | 3297 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, |
| 3354 | .NOT_SAME_DEVICE => return error.RenameAcrossMountPoints, | |
| 3298 | .NOT_SAME_DEVICE => return error.CrossDevice, | |
| 3355 | 3299 | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, |
| 3356 | 3300 | .DIRECTORY_NOT_EMPTY => return error.PathAlreadyExists, |
| 3357 | 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 | 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 | 364 | pub const RaiseError = UnexpectedError; |
| 466 | 365 | |
| 467 | 366 | pub fn raise(sig: SIG) RaiseError!void { |
| ... | ... | @@ -1695,7 +1594,7 @@ pub const FanotifyMarkError = error{ |
| 1695 | 1594 | NotDir, |
| 1696 | 1595 | OperationUnsupported, |
| 1697 | 1596 | PermissionDenied, |
| 1698 | NotSameFileSystem, | |
| 1597 | CrossDevice, | |
| 1699 | 1598 | NameTooLong, |
| 1700 | 1599 | } || UnexpectedError; |
| 1701 | 1600 | |
| ... | ... | @@ -1735,7 +1634,7 @@ pub fn fanotify_markZ( |
| 1735 | 1634 | .NOTDIR => return error.NotDir, |
| 1736 | 1635 | .OPNOTSUPP => return error.OperationUnsupported, |
| 1737 | 1636 | .PERM => return error.PermissionDenied, |
| 1738 | .XDEV => return error.NotSameFileSystem, | |
| 1637 | .XDEV => return error.CrossDevice, | |
| 1739 | 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 | 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 | 36 | test "getuid" { |
| 47 | 37 | if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; |
| 48 | 38 | _ = posix.getuid(); |
lib/std/std.zig-6| ... | ... | @@ -137,12 +137,6 @@ pub const Options = struct { |
| 137 | 137 | |
| 138 | 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 | 140 | /// By default, std.http.Client will support HTTPS connections. Set this option to `true` to |
| 147 | 141 | /// disable TLS support. |
| 148 | 142 | /// |
lib/std/testing.zig+1-1| ... | ... | @@ -631,7 +631,7 @@ pub const TmpDir = struct { |
| 631 | 631 | pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir { |
| 632 | 632 | comptime assert(builtin.is_test); |
| 633 | 633 | var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; |
| 634 | std.crypto.random.bytes(&random_bytes); | |
| 634 | io.random(&random_bytes); | |
| 635 | 635 | var sub_path: [TmpDir.sub_path_len]u8 = undefined; |
| 636 | 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 | 2942 | .none => |none| { |
| 2943 | 2943 | assert(none.tmp_artifact_directory == null); |
| 2944 | 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 | 2946 | const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int); |
| 2947 | 2947 | const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path}); |
| 2948 | 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 | 3023 | |
| 3024 | 3024 | // Compile the artifacts to a temporary directory. |
| 3025 | 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 | 3027 | const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int); |
| 3028 | 3028 | const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path}); |
| 3029 | 3029 | const handle = comp.dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{}) catch |err| { |
| ... | ... | @@ -3460,7 +3460,7 @@ fn renameTmpIntoCache( |
| 3460 | 3460 | }, |
| 3461 | 3461 | else => return error.AccessDenied, |
| 3462 | 3462 | }, |
| 3463 | error.PathAlreadyExists => { | |
| 3463 | error.DirNotEmpty => { | |
| 3464 | 3464 | try cache_directory.handle.deleteTree(io, o_sub_path); |
| 3465 | 3465 | continue; |
| 3466 | 3466 | }, |
| ... | ... | @@ -5759,7 +5759,11 @@ pub fn translateC( |
| 5759 | 5759 | |
| 5760 | 5760 | const gpa = comp.gpa; |
| 5761 | 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 | 5767 | const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ tmp_basename; |
| 5764 | 5768 | const cache_dir = comp.dirs.local_cache.handle; |
| 5765 | 5769 | var cache_tmp_dir = try cache_dir.createDirPathOpen(io, tmp_sub_path, .{}); |
| ... | ... | @@ -6889,8 +6893,13 @@ fn spawnZigRc( |
| 6889 | 6893 | } |
| 6890 | 6894 | |
| 6891 | 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 | 6902 | const s = fs.path.sep_str; |
| 6893 | const rand_int = std.crypto.random.int(u64); | |
| 6894 | 6903 | if (comp.dirs.local_cache.path) |p| { |
| 6895 | 6904 | return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix }); |
| 6896 | 6905 | } else { |
src/Package.zig+2-2| ... | ... | @@ -14,9 +14,9 @@ pub const Fingerprint = packed struct(u64) { |
| 14 | 14 | id: u32, |
| 15 | 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 | 18 | return .{ |
| 19 | .id = std.crypto.random.intRangeLessThan(u32, 1, 0xffffffff), | |
| 19 | .id = rng.intRangeLessThan(u32, 1, 0xffffffff), | |
| 20 | 20 | .checksum = std.hash.Crc32.hash(name), |
| 21 | 21 | }; |
| 22 | 22 | } |
src/Package/Fetch.zig+14-4| ... | ... | @@ -494,7 +494,11 @@ fn runResource( |
| 494 | 494 | const eb = &f.error_bundle; |
| 495 | 495 | const s = fs.path.sep_str; |
| 496 | 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 | 502 | const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(rand_int); |
| 499 | 503 | |
| 500 | 504 | const package_sub_path = blk: { |
| ... | ... | @@ -690,7 +694,9 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void { |
| 690 | 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 | 700 | .allow_missing_paths_field = f.allow_missing_paths_field, |
| 695 | 701 | .allow_missing_fingerprint = f.allow_missing_fingerprint, |
| 696 | 702 | .allow_name_string = f.allow_name_string, |
| ... | ... | @@ -1305,7 +1311,11 @@ fn unzip( |
| 1305 | 1311 | zip_path[prefix.len + random_len ..].* = suffix.*; |
| 1306 | 1312 | |
| 1307 | 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 | 1319 | zip_path[prefix.len..][0..random_len].* = std.fmt.hex(random_integer); |
| 1310 | 1320 | |
| 1311 | 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 | 1476 | }; |
| 1467 | 1477 | continue; |
| 1468 | 1478 | }, |
| 1469 | error.PathAlreadyExists, error.AccessDenied => { | |
| 1479 | error.DirNotEmpty, error.AccessDenied => { | |
| 1470 | 1480 | // Package has been already downloaded and may already be in use on the system. |
| 1471 | 1481 | cache_dir.deleteTree(io, tmp_dir_sub_path) catch { |
| 1472 | 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 | 57 | |
| 58 | 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 | 61 | const main_node_index = ast.nodeData(.root).node; |
| 62 | 62 | |
| 63 | 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 | 87 | defer p.dependencies.deinit(gpa); |
| 88 | 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 | 91 | error.ParseFailure => assert(p.errors.items.len > 0), |
| 92 | 92 | else => |e| return e, |
| 93 | 93 | }; |
| ... | ... | @@ -157,7 +157,7 @@ const Parse = struct { |
| 157 | 157 | |
| 158 | 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 | 161 | const ast = p.ast; |
| 162 | 162 | const main_token = ast.nodeMainToken(node); |
| 163 | 163 | |
| ... | ... | @@ -217,13 +217,13 @@ const Parse = struct { |
| 217 | 217 | if (fingerprint) |n| { |
| 218 | 218 | if (!n.validate(p.name)) { |
| 219 | 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 | 223 | p.id = n.id; |
| 224 | 224 | } else if (!p.allow_missing_fingerprint) { |
| 225 | 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 | 228 | } else { |
| 229 | 229 | p.id = 0; |
| ... | ... | @@ -623,7 +623,9 @@ test "basic" { |
| 623 | 623 | |
| 624 | 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 | 629 | defer manifest.deinit(gpa); |
| 628 | 630 | |
| 629 | 631 | try testing.expect(manifest.errors.len == 0); |
| ... | ... | @@ -666,7 +668,9 @@ test "minimum_zig_version" { |
| 666 | 668 | |
| 667 | 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 | 674 | defer manifest.deinit(gpa); |
| 671 | 675 | |
| 672 | 676 | try testing.expect(manifest.errors.len == 0); |
| ... | ... | @@ -698,7 +702,9 @@ test "minimum_zig_version - invalid version" { |
| 698 | 702 | |
| 699 | 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 | 708 | defer manifest.deinit(gpa); |
| 703 | 709 | |
| 704 | 710 | try testing.expect(manifest.errors.len == 1); |
src/link.zig+6-1| ... | ... | @@ -616,8 +616,13 @@ pub const File = struct { |
| 616 | 616 | // it will return ETXTBSY. So instead, we copy the file, atomically rename it |
| 617 | 617 | // over top of the exe path, and then proceed normally. This changes the inode, |
| 618 | 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 | 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 | 627 | defer gpa.free(tmp_sub_path); |
| 623 | 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 | 1636 | const err = switch (first_err) { |
| 1637 | 1637 | error.NameTooLong => err: { |
| 1638 | 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 | 1644 | const rsp_path = "tmp" ++ s ++ std.fmt.hex(rand_int) ++ ".rsp"; |
| 1641 | 1645 | |
| 1642 | 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 | 3395 | // "-" is stdin. Dump it to a real file. |
| 3396 | 3396 | const sep = fs.path.sep_str; |
| 3397 | 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 | 3400 | try dirs.local_cache.handle.createDirPath(io, "tmp"); |
| 3401 | 3401 | |
| ... | ... | @@ -4433,7 +4433,7 @@ fn runOrTest( |
| 4433 | 4433 | try argv.append(exe_path); |
| 4434 | 4434 | if (arg_mode == .zig_test) { |
| 4435 | 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 | 4439 | } else { |
| ... | ... | @@ -4763,7 +4763,8 @@ fn cmdInit(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! |
| 4763 | 4763 | const cwd_basename = fs.path.basename(cwd_path); |
| 4764 | 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 | 4769 | switch (template) { |
| 4769 | 4770 | .example => { |
| ... | ... | @@ -4919,7 +4920,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 4919 | 4920 | |
| 4920 | 4921 | try child_argv.appendSlice(&.{ |
| 4921 | 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 | 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 | 4938 | // the strategy is to choose a temporary file name ahead of time, and then |
| 4938 | 4939 | // read this file in the parent to obtain the results, in the case the child |
| 4939 | 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 | 4942 | try child_argv.append("-Z" ++ results_tmp_file_nonce); |
| 4942 | 4943 | |
| 4943 | 4944 | var color: Color = .auto; |
| ... | ... | @@ -7223,7 +7224,7 @@ fn createDependenciesModule( |
| 7223 | 7224 | ) !*Package.Module { |
| 7224 | 7225 | // Atomically create the file in a directory named after the hash of its contents. |
| 7225 | 7226 | const basename = "dependencies.zig"; |
| 7226 | const rand_int = std.crypto.random.int(u64); | |
| 7227 | const rand_int = randInt(io, u64); | |
| 7227 | 7228 | const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int); |
| 7228 | 7229 | { |
| 7229 | 7230 | var tmp_dir = try dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{}); |
| ... | ... | @@ -7339,6 +7340,8 @@ fn loadManifest( |
| 7339 | 7340 | io: Io, |
| 7340 | 7341 | options: LoadManifestOptions, |
| 7341 | 7342 | ) !struct { Package.Manifest, Ast } { |
| 7343 | const rng: std.Random.IoSource = .{ .io = io }; | |
| 7344 | ||
| 7342 | 7345 | const manifest_bytes = while (true) { |
| 7343 | 7346 | break options.dir.readFileAllocOptions( |
| 7344 | 7347 | io, |
| ... | ... | @@ -7360,15 +7363,13 @@ fn loadManifest( |
| 7360 | 7363 | , .{ |
| 7361 | 7364 | options.root_name, |
| 7362 | 7365 | build_options.version, |
| 7363 | Package.Fingerprint.generate(options.root_name).int(), | |
| 7366 | Package.Fingerprint.generate(rng.interface(), options.root_name).int(), | |
| 7364 | 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 | 7370 | continue; |
| 7368 | 7371 | }, |
| 7369 | else => |e| fatal("unable to load {s}: {s}", .{ | |
| 7370 | Package.Manifest.basename, @errorName(e), | |
| 7371 | }), | |
| 7372 | else => |e| fatal("unable to load {s}: {t}", .{ Package.Manifest.basename, e }), | |
| 7372 | 7373 | }; |
| 7373 | 7374 | }; |
| 7374 | 7375 | var ast = try Ast.parse(gpa, manifest_bytes, .zon); |
| ... | ... | @@ -7379,7 +7380,7 @@ fn loadManifest( |
| 7379 | 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 | 7384 | errdefer manifest.deinit(gpa); |
| 7384 | 7385 | |
| 7385 | 7386 | if (manifest.errors.len > 0) { |
| ... | ... | @@ -7632,3 +7633,9 @@ fn setThreadLimit(n: usize) void { |
| 7632 | 7633 | threaded_impl_ptr.setAsyncLimit(limit); |
| 7633 | 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 | 10 | |
| 11 | 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 | 16 | while (true) { |
| 16 | 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 | 52 | |
| 53 | 53 | const fuzz_seed = b.option(u64, "seed", "Seed to use for the PRNG (default: random)") orelse seed: { |
| 54 | 54 | var buf: [8]u8 = undefined; |
| 55 | try std.posix.getrandom(&buf); | |
| 55 | b.graph.io.random(&buf); | |
| 56 | 56 | break :seed std.mem.readInt(u64, &buf, builtin.cpu.arch.endian()); |
| 57 | 57 | }; |
| 58 | 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 | 5 | |
| 6 | 6 | pub fn main(init: std.process.Init) !void { |
| 7 | 7 | const gpa = init.gpa; |
| 8 | const io = init.io; | |
| 8 | 9 | const args = try init.minimal.args.toSlice(init.arena.allocator()); |
| 9 | 10 | |
| 10 | 11 | if (args.len < 2) return error.MissingArgs; |
| ... | ... | @@ -23,7 +24,7 @@ pub fn main(init: std.process.Init) !void { |
| 23 | 24 | if (args.len < 4) { |
| 24 | 25 | rand_seed = true; |
| 25 | 26 | var buf: [8]u8 = undefined; |
| 26 | try std.posix.getrandom(&buf); | |
| 27 | io.random(&buf); | |
| 27 | 28 | break :seed std.mem.readInt(u64, &buf, builtin.cpu.arch.endian()); |
| 28 | 29 | } |
| 29 | 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 | 65 | |
| 66 | 66 | const fuzz_seed = b.option(u64, "seed", "Seed to use for the PRNG (default: random)") orelse seed: { |
| 67 | 67 | var buf: [8]u8 = undefined; |
| 68 | try std.posix.getrandom(&buf); | |
| 68 | b.graph.io.random(&buf); | |
| 69 | 69 | break :seed std.mem.readInt(u64, &buf, builtin.cpu.arch.endian()); |
| 70 | 70 | }; |
| 71 | 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 | 22 | const seed_arg = it.next() orelse { |
| 23 | 23 | rand_seed = true; |
| 24 | 24 | var buf: [8]u8 = undefined; |
| 25 | try std.posix.getrandom(&buf); | |
| 25 | io.random(&buf); | |
| 26 | 26 | break :seed std.mem.readInt(u64, &buf, builtin.cpu.arch.endian()); |
| 27 | 27 | }; |
| 28 | 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 | 78 | const code = try parseManifest(arena, source_bytes); |
| 79 | 79 | const source = stripManifest(source_bytes); |
| 80 | 80 | |
| 81 | const tmp_dir_path = try std.fmt.allocPrint(arena, "{s}/tmp/{x}", .{ | |
| 82 | cache_root, std.crypto.random.int(u64), | |
| 83 | }); | |
| 81 | var random_integer: u64 = undefined; | |
| 82 | io.random(@ptrCast(&random_integer)); | |
| 83 | ||
| 84 | const tmp_dir_path = try std.fmt.allocPrint(arena, "{s}/tmp/{x}", .{ cache_root, random_integer }); | |
| 84 | 85 | Dir.cwd().createDirPath(io, tmp_dir_path) catch |err| |
| 85 | 86 | fatal("unable to create tmp dir '{s}': {t}", .{ tmp_dir_path, err }); |
| 86 | 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 | 100 | const prog_node = std.Progress.start(io, .{}); |
| 101 | 101 | defer prog_node.end(); |
| 102 | 102 | |
| 103 | const rand_int = std.crypto.random.int(u64); | |
| 103 | const rand_int = rand64(io); | |
| 104 | 104 | const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int); |
| 105 | 105 | var tmp_dir = try Dir.cwd().createDirPathOpen(io, tmp_dir_path, .{}); |
| 106 | 106 | defer { |
| ... | ... | @@ -452,20 +452,19 @@ const Eval = struct { |
| 452 | 452 | std.debug.assert(eval.target.backend == .sema); |
| 453 | 453 | return; |
| 454 | 454 | }; |
| 455 | const io = eval.io; | |
| 455 | 456 | |
| 456 | 457 | const binary_path = switch (eval.target.backend) { |
| 457 | 458 | .sema => unreachable, |
| 458 | 459 | .selfhosted, .llvm => emitted_path, |
| 459 | 460 | .cbe => bin: { |
| 460 | const rand_int = std.crypto.random.int(u64); | |
| 461 | const rand_int = rand64(io); | |
| 461 | 462 | const out_bin_name = "./out_" ++ std.fmt.hex(rand_int); |
| 462 | 463 | try eval.buildCOutput(emitted_path, out_bin_name, prog_node); |
| 463 | 464 | break :bin out_bin_name; |
| 464 | 465 | }, |
| 465 | 466 | }; |
| 466 | 467 | |
| 467 | const io = eval.io; | |
| 468 | ||
| 469 | 468 | var argv_buf: [2][]const u8 = undefined; |
| 470 | 469 | const argv: []const []const u8, const is_foreign: bool = sw: switch (std.zig.system.getExternalExecutor( |
| 471 | 470 | io, |
| ... | ... | @@ -957,3 +956,9 @@ fn parseExpectedError(str: []const u8, l: usize) Case.ExpectedError { |
| 957 | 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 | } |