| author | |
| committer | |
| log | 81a35a86ea9385016f371213a2b72b14902bb955 |
| tree | 730e69aff40ca5abd5f1ac8b6b51295f3521afe4 |
| parent | e3e9c7c33c029368ad644619ed20f3d7cccd3a47 |
and use a thread-local CSPRNG for the former.7 files changed, 135 insertions(+), 17 deletions(-)
lib/std/Build/Step/Run.zig+4-2| ... | @@ -984,7 +984,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void { | ... | @@ -984,7 +984,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 984 | }; | 984 | }; |
| 985 | 985 | ||
| 986 | // We do not know the final output paths yet, use temp paths to run the command. | 986 | // We do not know the final output paths yet, use temp paths to run the command. |
| 987 | const rand_int = std.crypto.random.int(u64); | 987 | var rand_int: u64 = undefined; |
| 988 | io.random(@ptrCast(&rand_int)); | ||
| 988 | const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); | 989 | const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); |
| 989 | 990 | ||
| 990 | for (output_placeholders.items) |placeholder| { | 991 | for (output_placeholders.items) |placeholder| { |
| ... | @@ -1128,7 +1129,8 @@ pub fn rerunInFuzzMode( | ... | @@ -1128,7 +1129,8 @@ pub fn rerunInFuzzMode( |
| 1128 | } | 1129 | } |
| 1129 | 1130 | ||
| 1130 | const has_side_effects = false; | 1131 | const has_side_effects = false; |
| 1131 | const rand_int = std.crypto.random.int(u64); | 1132 | var rand_int: u64 = undefined; |
| 1133 | io.random(@ptrCast(&rand_int)); | ||
| 1132 | const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); | 1134 | const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); |
| 1133 | try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, .{ | 1135 | try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, .{ |
| 1134 | .progress_node = prog_node, | 1136 | .progress_node = prog_node, |
lib/std/Build/Step/WriteFile.zig+2-1| ... | @@ -293,7 +293,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void { | ... | @@ -293,7 +293,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 293 | .tmp => { | 293 | .tmp => { |
| 294 | step.result_cached = false; | 294 | step.result_cached = false; |
| 295 | 295 | ||
| 296 | const rand_int = std.crypto.random.int(u64); | 296 | var rand_int: u64 = undefined; |
| 297 | io.random(@ptrCast(&rand_int)); | ||
| 297 | const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); | 298 | const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); |
| 298 | 299 | ||
| 299 | write_file.generated_directory.path = try b.cache_root.join(arena, &.{tmp_dir_sub_path}); | 300 | write_file.generated_directory.path = try b.cache_root.join(arena, &.{tmp_dir_sub_path}); |
lib/std/Io.zig+30-4| ... | @@ -731,7 +731,8 @@ pub const VTable = struct { | ... | @@ -731,7 +731,8 @@ pub const VTable = struct { |
| 731 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, | 731 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, |
| 732 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, | 732 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, |
| 733 | 733 | ||
| 734 | random: *const fn (?*anyopaque, buffer: []u8) RandomError!void, | 734 | random: *const fn (?*anyopaque, buffer: []u8) void, |
| 735 | randomSecure: *const fn (?*anyopaque, buffer: []u8) RandomSecureError!void, | ||
| 735 | 736 | ||
| 736 | netListenIp: *const fn (?*anyopaque, address: net.IpAddress, net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server, | 737 | netListenIp: *const fn (?*anyopaque, address: net.IpAddress, net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server, |
| 737 | netAccept: *const fn (?*anyopaque, server: net.Socket.Handle) net.Server.AcceptError!net.Stream, | 738 | netAccept: *const fn (?*anyopaque, server: net.Socket.Handle) net.Server.AcceptError!net.Stream, |
| ... | @@ -2245,9 +2246,34 @@ pub fn unlockStderr(io: Io) void { | ... | @@ -2245,9 +2246,34 @@ pub fn unlockStderr(io: Io) void { |
| 2245 | return io.vtable.unlockStderr(io.userdata); | 2246 | return io.vtable.unlockStderr(io.userdata); |
| 2246 | } | 2247 | } |
| 2247 | 2248 | ||
| 2248 | pub const RandomError = error{EntropyUnavailable} || Cancelable; | 2249 | /// Obtains entropy. |
| 2249 | 2250 | /// | |
| 2251 | /// The implementation *may* store RNG state in process memory and use it to | ||
| 2252 | /// fill `buffer`. | ||
| 2253 | /// | ||
| 2254 | /// The degree to which the entropy is cryptographically secure is determined | ||
| 2255 | /// by the `Io` implementation. | ||
| 2256 | /// | ||
| 2250 | /// Threadsafe. | 2257 | /// Threadsafe. |
| 2251 | pub fn random(io: Io, buffer: []u8) RandomError!void { | 2258 | /// |
| 2259 | /// See also `randomSecure`. | ||
| 2260 | pub fn random(io: Io, buffer: []u8) void { | ||
| 2252 | return io.vtable.random(io.userdata, buffer); | 2261 | return io.vtable.random(io.userdata, buffer); |
| 2253 | } | 2262 | } |
| 2263 | |||
| 2264 | pub const RandomSecureError = error{EntropyUnavailable} || Cancelable; | ||
| 2265 | |||
| 2266 | /// Obtains cryptographically secure entropy from outside the process. | ||
| 2267 | /// | ||
| 2268 | /// Always makes a syscall, or otherwise avoids dependency on process memory, | ||
| 2269 | /// in order to obtain fresh randomness. Does not rely on stored RNG state. | ||
| 2270 | /// | ||
| 2271 | /// Does not have any fallback mechanisms; returns `error.EntropyUnavailable` | ||
| 2272 | /// if any problems occur. | ||
| 2273 | /// | ||
| 2274 | /// Threadsafe. | ||
| 2275 | /// | ||
| 2276 | /// See also `random`. | ||
| 2277 | pub fn randomSecure(io: Io, buffer: []u8) RandomSecureError!void { | ||
| 2278 | return io.vtable.randomSecure(io.userdata, buffer); | ||
| 2279 | } |
lib/std/Io/Dir.zig+3-1| ... | @@ -1098,8 +1098,10 @@ pub fn symLinkAtomic( | ... | @@ -1098,8 +1098,10 @@ pub fn symLinkAtomic( |
| 1098 | 1098 | ||
| 1099 | const temp_path = temp_path_buf[0..temp_path_len]; | 1099 | const temp_path = temp_path_buf[0..temp_path_len]; |
| 1100 | 1100 | ||
| 1101 | var random_integer: u64 = undefined; | ||
| 1102 | |||
| 1101 | while (true) { | 1103 | while (true) { |
| 1102 | const random_integer = std.crypto.random.int(u64); | 1104 | io.random(@ptrCast(&random_integer)); |
| 1103 | temp_path[dirname.len + 1 ..][0..rand_len].* = std.fmt.hex(random_integer); | 1105 | temp_path[dirname.len + 1 ..][0..rand_len].* = std.fmt.hex(random_integer); |
| 1104 | 1106 | ||
| 1105 | if (dir.symLink(io, target_path, temp_path, flags)) { | 1107 | if (dir.symLink(io, target_path, temp_path, flags)) { |
lib/std/Io/Threaded.zig+79-8| ... | @@ -67,6 +67,21 @@ environ: Environ, | ... | @@ -67,6 +67,21 @@ environ: Environ, |
| 67 | null_file: NullFile = .{}, | 67 | null_file: NullFile = .{}, |
| 68 | random_file: RandomFile = .{}, | 68 | random_file: RandomFile = .{}, |
| 69 | 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 | }; | ||
| 84 | |||
| 70 | pub const Argv0 = switch (native_os) { | 85 | pub const Argv0 = switch (native_os) { |
| 71 | .openbsd, .haiku => struct { | 86 | .openbsd, .haiku => struct { |
| 72 | value: ?[*:0]const u8, | 87 | value: ?[*:0]const u8, |
| ... | @@ -595,7 +610,7 @@ const Thread = struct { | ... | @@ -595,7 +610,7 @@ const Thread = struct { |
| 595 | /// Always released when `Status.cancelation` is set to `.parked`. | 610 | /// Always released when `Status.cancelation` is set to `.parked`. |
| 596 | futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn, | 611 | futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn, |
| 597 | 612 | ||
| 598 | csprng: std.Random.DefaultCsprng, | 613 | csprng: Csprng, |
| 599 | 614 | ||
| 600 | const Handle = Handle: { | 615 | const Handle = Handle: { |
| 601 | if (std.Thread.use_pthreads) break :Handle std.c.pthread_t; | 616 | if (std.Thread.use_pthreads) break :Handle std.c.pthread_t; |
| ... | @@ -1326,10 +1341,7 @@ fn worker(t: *Threaded) void { | ... | @@ -1326,10 +1341,7 @@ fn worker(t: *Threaded) void { |
| 1326 | }), | 1341 | }), |
| 1327 | .cancel_protection = .unblocked, | 1342 | .cancel_protection = .unblocked, |
| 1328 | .futex_waiter = undefined, | 1343 | .futex_waiter = undefined, |
| 1329 | .csprng = .{ | 1344 | .csprng = .{}, |
| 1330 | .state = undefined, | ||
| 1331 | .offset = std.math.maxInt(usize), | ||
| 1332 | }, | ||
| 1333 | }; | 1345 | }; |
| 1334 | Thread.current = &thread; | 1346 | Thread.current = &thread; |
| 1335 | 1347 | ||
| ... | @@ -1484,6 +1496,7 @@ pub fn io(t: *Threaded) Io { | ... | @@ -1484,6 +1496,7 @@ pub fn io(t: *Threaded) Io { |
| 1484 | .sleep = sleep, | 1496 | .sleep = sleep, |
| 1485 | 1497 | ||
| 1486 | .random = random, | 1498 | .random = random, |
| 1499 | .randomSecure = randomSecure, | ||
| 1487 | 1500 | ||
| 1488 | .netListenIp = switch (native_os) { | 1501 | .netListenIp = switch (native_os) { |
| 1489 | .windows => netListenIpWindows, | 1502 | .windows => netListenIpWindows, |
| ... | @@ -1634,6 +1647,7 @@ pub fn ioBasic(t: *Threaded) Io { | ... | @@ -1634,6 +1647,7 @@ pub fn ioBasic(t: *Threaded) Io { |
| 1634 | .sleep = sleep, | 1647 | .sleep = sleep, |
| 1635 | 1648 | ||
| 1636 | .random = random, | 1649 | .random = random, |
| 1650 | .randomSecure = randomSecure, | ||
| 1637 | 1651 | ||
| 1638 | .netListenIp = netListenIpUnavailable, | 1652 | .netListenIp = netListenIpUnavailable, |
| 1639 | .netListenUnix = netListenUnixUnavailable, | 1653 | .netListenUnix = netListenUnixUnavailable, |
| ... | @@ -3584,8 +3598,9 @@ fn atomicFileInit( | ... | @@ -3584,8 +3598,9 @@ fn atomicFileInit( |
| 3584 | dir: Dir, | 3598 | dir: Dir, |
| 3585 | close_dir_on_deinit: bool, | 3599 | close_dir_on_deinit: bool, |
| 3586 | ) Dir.CreateFileAtomicError!File.Atomic { | 3600 | ) Dir.CreateFileAtomicError!File.Atomic { |
| 3601 | var random_integer: u64 = undefined; | ||
| 3587 | while (true) { | 3602 | while (true) { |
| 3588 | const random_integer = std.crypto.random.int(u64); | 3603 | t_io.random(@ptrCast(&random_integer)); |
| 3589 | const tmp_sub_path = std.fmt.hex(random_integer); | 3604 | const tmp_sub_path = std.fmt.hex(random_integer); |
| 3590 | const file = dir.createFile(t_io, &tmp_sub_path, .{ | 3605 | const file = dir.createFile(t_io, &tmp_sub_path, .{ |
| 3591 | .permissions = permissions, | 3606 | .permissions = permissions, |
| ... | @@ -12468,7 +12483,8 @@ fn lookupDns( | ... | @@ -12468,7 +12483,8 @@ fn lookupDns( |
| 12468 | 12483 | ||
| 12469 | for (family_records) |fr| { | 12484 | for (family_records) |fr| { |
| 12470 | if (options.family != fr.af) { | 12485 | if (options.family != fr.af) { |
| 12471 | const entropy = std.crypto.random.array(u8, 2); | 12486 | var entropy: [2]u8 = undefined; |
| 12487 | random(t, &entropy); | ||
| 12472 | const len = writeResolutionQuery(&query_buffers[nq], 0, lookup_canon_name, 1, fr.rr, entropy); | 12488 | const len = writeResolutionQuery(&query_buffers[nq], 0, lookup_canon_name, 1, fr.rr, entropy); |
| 12473 | queries_buffer[nq] = query_buffers[nq][0..len]; | 12489 | queries_buffer[nq] = query_buffers[nq][0..len]; |
| 12474 | nq += 1; | 12490 | nq += 1; |
| ... | @@ -15018,7 +15034,62 @@ pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 { | ... | @@ -15018,7 +15034,62 @@ pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 { |
| 15018 | return @field(t.environ.string, name); | 15034 | return @field(t.environ.string, name); |
| 15019 | } | 15035 | } |
| 15020 | 15036 | ||
| 15021 | fn random(userdata: ?*anyopaque, buffer: []u8) Io.RandomError!void { | 15037 | fn random(userdata: ?*anyopaque, buffer: []u8) void { |
| 15038 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | ||
| 15039 | const thread = Thread.current orelse return randomMainThread(t, buffer); | ||
| 15040 | if (!thread.csprng.isInitialized()) { | ||
| 15041 | @branchHint(.unlikely); | ||
| 15042 | var seed: [Csprng.seed_len]u8 = undefined; | ||
| 15043 | randomMainThread(t, &seed); | ||
| 15044 | thread.csprng.rng = .init(seed); | ||
| 15045 | } | ||
| 15046 | thread.csprng.rng.fill(buffer); | ||
| 15047 | } | ||
| 15048 | |||
| 15049 | fn randomMainThread(t: *Threaded, buffer: []u8) void { | ||
| 15050 | t.mutex.lock(); | ||
| 15051 | defer t.mutex.unlock(); | ||
| 15052 | |||
| 15053 | if (!t.csprng.isInitialized()) { | ||
| 15054 | @branchHint(.unlikely); | ||
| 15055 | var seed: [Csprng.seed_len]u8 = undefined; | ||
| 15056 | { | ||
| 15057 | t.mutex.unlock(); | ||
| 15058 | defer t.mutex.lock(); | ||
| 15059 | |||
| 15060 | const prev = swapCancelProtection(t, .blocked); | ||
| 15061 | defer _ = swapCancelProtection(t, prev); | ||
| 15062 | |||
| 15063 | randomSecure(t, &seed) catch |err| switch (err) { | ||
| 15064 | error.Canceled => unreachable, | ||
| 15065 | error.EntropyUnavailable => { | ||
| 15066 | seed = @splat(0); | ||
| 15067 | std.mem.writeInt(posix.pid_t, seed[0..@sizeOf(posix.pid_t)], posix.system.getpid(), .native); | ||
| 15068 | const i_1 = @sizeOf(posix.pid_t); | ||
| 15069 | |||
| 15070 | var ts: posix.timespec = undefined; | ||
| 15071 | const Sec = @TypeOf(ts.sec); | ||
| 15072 | const Nsec = @TypeOf(ts.nsec); | ||
| 15073 | const i_2 = i_1 + @sizeOf(Sec); | ||
| 15074 | const i_3 = i_2 + @sizeOf(Nsec); | ||
| 15075 | switch (posix.errno(posix.system.clock_gettime(.REALTIME, &ts))) { | ||
| 15076 | .SUCCESS => { | ||
| 15077 | std.mem.writeInt(Sec, seed[i_1..][0..@sizeOf(Sec)], ts.sec, .native); | ||
| 15078 | std.mem.writeInt(Nsec, seed[i_2..][0..@sizeOf(Nsec)], ts.nsec, .native); | ||
| 15079 | }, | ||
| 15080 | else => {}, | ||
| 15081 | } | ||
| 15082 | std.mem.writeInt(usize, seed[i_3..][0..@sizeOf(usize)], @intFromPtr(t), .native); | ||
| 15083 | }, | ||
| 15084 | }; | ||
| 15085 | } | ||
| 15086 | t.csprng.rng = .init(seed); | ||
| 15087 | } | ||
| 15088 | |||
| 15089 | t.csprng.rng.fill(buffer); | ||
| 15090 | } | ||
| 15091 | |||
| 15092 | fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void { | ||
| 15022 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 15093 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 15023 | 15094 | ||
| 15024 | if (is_windows) { | 15095 | if (is_windows) { |
lib/std/Random.zig+16| ... | @@ -33,6 +33,22 @@ pub const ziggurat = @import("Random/ziggurat.zig"); | ... | @@ -33,6 +33,22 @@ pub const ziggurat = @import("Random/ziggurat.zig"); |
| 33 | ptr: *anyopaque, | 33 | ptr: *anyopaque, |
| 34 | fillFn: *const fn (ptr: *anyopaque, buf: []u8) void, | 34 | fillFn: *const fn (ptr: *anyopaque, buf: []u8) void, |
| 35 | 35 | ||
| 36 | pub const IoSource = struct { | ||
| 37 | io: std.Io, | ||
| 38 | |||
| 39 | pub fn interface(this: *const @This()) std.Random { | ||
| 40 | return .{ | ||
| 41 | .ptr = 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 | |||
| 36 | pub fn init(pointer: anytype, comptime fillFn: fn (ptr: @TypeOf(pointer), buf: []u8) void) Random { | 52 | pub fn init(pointer: anytype, comptime fillFn: fn (ptr: @TypeOf(pointer), buf: []u8) void) Random { |
| 37 | const Ptr = @TypeOf(pointer); | 53 | const Ptr = @TypeOf(pointer); |
| 38 | assert(@typeInfo(Ptr) == .pointer); // Must be a pointer | 54 | assert(@typeInfo(Ptr) == .pointer); // Must be a pointer |
lib/std/Random/ChaCha.zig+1-1| ... | @@ -20,7 +20,7 @@ pub const secret_seed_length = Cipher.key_length; | ... | @@ -20,7 +20,7 @@ pub const secret_seed_length = Cipher.key_length; |
| 20 | 20 | ||
| 21 | /// The seed must be uniform, secret and `secret_seed_length` bytes long. | 21 | /// The seed must be uniform, secret and `secret_seed_length` bytes long. |
| 22 | pub fn init(secret_seed: [secret_seed_length]u8) Self { | 22 | pub fn init(secret_seed: [secret_seed_length]u8) Self { |
| 23 | var self = Self{ .state = undefined, .offset = 0 }; | 23 | var self: Self = .{ .state = undefined, .offset = 0 }; |
| 24 | Cipher.stream(&self.state, 0, secret_seed, nonce); | 24 | Cipher.stream(&self.state, 0, secret_seed, nonce); |
| 25 | return self; | 25 | return self; |
| 26 | } | 26 | } |