feature. See also
. The project being documented here (as the example) is the Zig library itself.
Threaded.randomSecure
fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void
File
Code
fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void {
const t: *Threaded = @ptrCast(@alignCast(userdata));
if (is_windows) {
if (buffer.len == 0) return;
// * introduces a dependency on bcryptprimitives.dll, which apparently
// runs a test suite every time it is loaded
// * heap allocates a 48-byte buffer, handling failure by returning NO_MEMORY in a BOOL
// despite the function being documented to always return TRUE
// * reads from "\\Device\\CNG" which then seeds a per-CPU AES CSPRNG
// Therefore, that function is avoided in favor of using the device directly.
const cng_device = try getCngDevice(t);
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
var i: usize = 0;
const syscall: Syscall = try .start();
while (true) {
const remaining_len = std.math.lossyCast(u32, buffer.len - i);
switch (windows.ntdll.NtDeviceIoControlFile(
cng_device,
null,
null,
null,
&io_status_block,
windows.IOCTL.KSEC.GEN_RANDOM,
null,
0,
buffer[i..].ptr,
remaining_len,
)) {
.SUCCESS => {
i += remaining_len;
if (buffer.len - i == 0) {
return syscall.finish();
} else {
try syscall.checkCancel();
continue;
}
},
.CANCELLED => {
try syscall.checkCancel();
continue;
},
else => return syscall.fail(error.EntropyUnavailable),
}
}
}
if (builtin.link_libc and @TypeOf(posix.system.arc4random_buf) != void) {
if (buffer.len == 0) return;
posix.system.arc4random_buf(buffer.ptr, buffer.len);
return;
}
if (native_os == .wasi) {
if (buffer.len == 0) return;
const syscall: Syscall = try .start();
while (true) switch (std.os.wasi.random_get(buffer.ptr, buffer.len)) {
.SUCCESS => return syscall.finish(),
.INTR => {
try syscall.checkCancel();
continue;
},
else => return syscall.fail(error.EntropyUnavailable),
};
}
if (@TypeOf(posix.system.getrandom) != void) {
const getrandom = if (use_libc_getrandom) std.c.getrandom else std.os.linux.getrandom;
var i: usize = 0;
const syscall: Syscall = try .start();
while (buffer.len - i != 0) {
const buf = buffer[i..];
const rc = getrandom(buf.ptr, buf.len, 0);
switch (posix.errno(rc)) {
.SUCCESS => {
syscall.finish();
const n: usize = @intCast(rc);
i += n;
continue;
},
.INTR => {
try syscall.checkCancel();
continue;
},
else => return syscall.fail(error.EntropyUnavailable),
}
}
return;
}
if (native_os == .emscripten) {
if (buffer.len == 0) return;
const err = posix.errno(std.c.getentropy(buffer.ptr, buffer.len));
switch (err) {
.SUCCESS => return,
else => return error.EntropyUnavailable,
}
}
if (native_os == .linux) {
comptime assert(use_dev_urandom);
const urandom_fd = try getRandomFd(t);
var i: usize = 0;
while (buffer.len - i != 0) {
const syscall: Syscall = try .start();
const rc = posix.system.read(urandom_fd, buffer[i..].ptr, buffer.len - i);
switch (posix.errno(rc)) {
.SUCCESS => {
syscall.finish();
const n: usize = @intCast(rc);
if (n == 0) return error.EntropyUnavailable;
i += n;
continue;
},
.INTR => {
try syscall.checkCancel();
continue;
},
else => return syscall.fail(error.EntropyUnavailable),
}
}
}
return error.EntropyUnavailable;
}