Randomness

July 16, 2026 ยท View on GitHub

Every secret the kernel generates, the IPC MAC key, the capability key material, the kernel's signing keypair, the ASLR offset, is drawn from one secure random path. That path combines a software CSPRNG with hardware entropy so it does not depend on any single source. This page documents the pipeline and what draws from it. The code is under src/crypto/random_api/ and src/crypto/util/rng/.

The secure path

get_bytes_secure (src/crypto/random_api/basic.rs:38) is the two-stage fill:

  get_bytes_secure(buffer):
      rng::fill_random_bytes(buffer)     // software CSPRNG output
      mix_hardware_entropy(buffer)       // XOR hardware entropy over it

The software stage fills the buffer from the kernel CSPRNG. The hardware stage (hardware_mix.rs:20) then mixes real hardware entropy into it by XOR, from two sources when they are present:

  mix_hardware_entropy(buffer):
      mix_virtio_rng(buffer)   // if the virtio-rng driver is available, XOR its bytes in
      mix_cpu_rng(buffer)      // if RDRAND/RDSEED is available, XOR CPU entropy in

XOR-combining is the reason the result is no weaker than its best source: an output byte is the software CSPRNG byte XOR the virtio-rng byte XOR the CPU-entropy byte, so an adversary would have to predict all of the available sources to predict the output. The CPU source prefers RDSEED over RDRAND (next_cpu_random), and the intermediate hardware buffers are zeroized after use so they do not linger on the stack. When a hardware source is not present its stage is skipped, and the software CSPRNG still fills the buffer.

Entropy sizing

get_bytes_checked (basic.rs:20) refuses to fill a buffer smaller than the requested minimum entropy (required_entropy_bytes, entropy_check.rs), returning BufferTooSmall rather than handing back fewer bits than the caller asked for. This keeps a caller that needs, say, 256 bits of key material from silently getting less.

What draws from it

The secure path is the single source for the kernel's secrets:

  IPC MAC key         ipc/nonos_channel/hash.rs   init_ipc_secret, fatal if the RNG fails
  kernel keypair      crypto/kernel_keys.rs        the Ed25519 signing key at init
  capability keys     capabilities/token/          the MAC key material
  ASLR offset         elf/aslr/                     the per-load base randomization

The IPC MAC seeding is fatal on RNG failure (see the IPC envelope): the kernel will not run with an unkeyed IPC path. The others draw the same way, so the quality of every kernel secret reduces to the quality of this one path, which is why it mixes hardware entropy rather than trusting the software CSPRNG alone.

Security analysis

Every kernel secret reduces to this one path, so the properties that matter are that the output is no weaker than its strongest source, that a short buffer cannot silently shrink the entropy, and that the intermediate hardware bytes do not linger.

The output is no weaker than its best available source. get_bytes_secure (src/crypto/random_api/basic.rs:38) first fills the buffer from the software CSPRNG, then mix_hardware_entropy (hardware_mix.rs:21) XORs in virtio-rng bytes when the driver is available and CPU entropy when RDRAND or RDSEED is present. Because the combine is XOR, an output byte is the software byte XOR the virtio byte XOR the CPU byte, so an adversary would have to predict every present source to predict the output, and a single trustworthy source is enough to save the result. The CPU source prefers RDSEED over RDRAND (next_cpu_random, hardware_mix.rs:57). When a hardware source is absent its stage is simply skipped and the software CSPRNG still fills the buffer, so the mix never lowers the quality below the software path.

A caller cannot silently get fewer bits than it asked for. get_bytes_checked (basic.rs:31) rejects a buffer smaller than required_entropy_bytes(min_entropy) (entropy_check.rs:17, ceiling-divides the requested bits to bytes) with CryptoError::BufferTooSmall rather than filling it, so a path that needs 256 bits of key material cannot accidentally be handed 128.

The hardware buffers are scrubbed after they are folded in. mix_virtio_rng zeros its 64-byte hardware buffer with volatile writes after XORing it into the output (hardware_mix.rs:38, zeroize), so the raw hardware draw does not sit on the stack after use. Key generation that has no usable entropy fails rather than falling back to a weak source: the crypto error set carries InsufficientEntropy precisely so wallet and mnemonic generation abort instead of producing predictable keys (src/crypto/error.rs:45).

The honest boundary is that the software CSPRNG is the floor, not a proven-unpredictable source on its own. On a machine with neither virtio-rng nor RDRAND/RDSEED, both hardware stages skip and the output is exactly the software CSPRNG's, so the strength of the result on such a machine rests entirely on that generator's seeding. The design mitigates this by mixing hardware entropy wherever it exists rather than trusting one source, but it does not manufacture entropy where the platform provides none, and get_bytes_secure itself returns Ok in that case rather than signalling the degraded posture; only the explicit key-generation paths escalate to InsufficientEntropy.

Debugging randomness

Randomness fails quietly by nature, so debugging is about knowing which stage ran. The two hardware stages are conditional: mix_virtio_rng returns early if virtio_rng::is_available() is false, and mix_cpu_rng returns early unless has_rdrand() or has_rdseed() is true (hardware_mix.rs:30, :42). So if you suspect the output is coming from the software CSPRNG alone, the question is whether either predicate is true on this machine, not whether the mix code ran, since the mix code silently does nothing when a source is absent. That absence is the expected, not the error, case.

The one place the RNG is allowed to be fatal is where a secret cannot safely be weak. IPC MAC seeding treats an RNG failure as fatal and refuses to run with an unkeyed IPC path (see the IPC envelope), and wallet and key generation surface CryptoError::InsufficientEntropy (error.rs:45) rather than a fallback key. So a boot that halts at IPC seeding points at the RNG failing at init, and an InsufficientEntropy from a key path means no hardware entropy was available on a path that refuses to proceed without it. A caller that gets BufferTooSmall from get_bytes_checked (basic.rs:32) has an entropy-sizing bug, asking for more bits than the buffer can hold, which is distinct from an entropy-source failure and is caught before any bytes are drawn.

Source map

  src/crypto/random_api/basic.rs           get_bytes_secure, get_bytes_checked, the two-stage fill
  src/crypto/random_api/hardware_mix.rs     the virtio-rng and CPU-entropy XOR mix and the scrub
  src/crypto/random_api/entropy_check.rs    the minimum-entropy sizing
  src/crypto/util/rng/entropy/hardware.rs   RDRAND / RDSEED access
  src/crypto/error.rs                       BufferTooSmall and InsufficientEntropy

Every reference above is verified against those trees. The IPC MAC key that fails fatally on RNG error is on the IPC envelope page, the kernel keypair drawn from this path is on the asymmetric page, and the ASLR offset it feeds is on the ELF loader pages.