zig/lib/std/crypto/tlcsprng.zig
Andrew Kelley 2e4b409f31 std: tlcsprng: cleanups & improvements
* get rid of the pointless fences
 * make seed_len 16 instead of 32, which is accurate since it was
   already padding the rest anyway; now we do 1 pad instead of 2.
 * secureZero to clear the AT_RANDOM auxval
 * add a flag root source files can use to disable the start code. This
   is in case people want to opt out of the initialization when they
   don't depend on it.
2020-12-18 12:22:46 -07:00

63 lines
2.5 KiB
Zig

// SPDX-License-Identifier: MIT
// Copyright (c) 2015-2020 Zig Contributors
// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
//! Thread-local cryptographically secure pseudo-random number generator.
//! This file has public declarations that are intended to be used internally
//! by the standard library; this namespace is not intended to be exposed
//! directly to standard library users.
const std = @import("std");
const root = @import("root");
const mem = std.mem;
/// We use this as a layer of indirection because global const pointers cannot
/// point to thread-local variables.
pub var interface = std.rand.Random{ .fillFn = tlsCsprngFill };
pub threadlocal var csprng_state: std.crypto.core.Gimli = undefined;
pub threadlocal var csprng_state_initialized = false;
fn tlsCsprngFill(r: *const std.rand.Random, buf: []u8) void {
if (std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf")) {
// arc4random is already a thread-local CSPRNG.
return std.c.arc4random_buf(buf.ptr, buf.len);
}
if (!csprng_state_initialized) {
var seed: [seed_len]u8 = undefined;
// Because we panic on getrandom() failing, we provide the opportunity
// to override the default seed function. This also makes
// `std.crypto.random` available on freestanding targets, provided that
// the `cryptoRandomSeed` function is provided.
if (@hasDecl(root, "cryptoRandomSeed")) {
root.cryptoRandomSeed(&seed);
} else {
defaultSeed(&seed);
}
init(seed);
}
if (buf.len != 0) {
csprng_state.squeeze(buf);
} else {
csprng_state.permute();
}
mem.set(u8, csprng_state.toSlice()[0..std.crypto.core.Gimli.RATE], 0);
}
fn defaultSeed(buffer: *[seed_len]u8) void {
std.os.getrandom(buffer) catch @panic("getrandom() failed to seed thread-local CSPRNG");
}
pub const seed_len = 16;
pub fn init(seed: [seed_len]u8) void {
var initial_state: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined;
mem.copy(u8, initial_state[0..seed_len], &seed);
mem.set(u8, initial_state[seed_len..], 0);
csprng_state = std.crypto.core.Gimli.init(initial_state);
// This is at the end so that accidental recursive dependencies result
// in stack overflows instead of invalid random data.
csprng_state_initialized = true;
}