Repository navigation
Bare metal: one ChaCha20 generator, keyed from the hardware, behind every reader - #1059
Merged
Merged
Conversation
…very reader #1051 made the rule that a secret comes only from a hardware generator, and kept it by reading the generator for every byte. That gave readers the hardware's raw output with nothing behind it, at its speed (an RNG200 is about 190 KB/s), and a key that waited for a generator held the Dis VM, so the console and every program on that VM stopped too. os/port/random.c is now a deterministic generator between the hardware and every reader -- #c/random, #c/notquiterandom, genrandom, prng, prngtry, truerand -- as in Linux, the BSDs and 9front: - ChaCha20 (RFC 8439) with fast key erasure, as libsec's hosted genrandom.c: each request takes 32 bytes of keystream as the next key first, and its output follows them; the state it came from is gone. - Keyed from hwrandom alone: key' = SHA-256(32 bytes of the old stream || 32 bytes from the hardware), every second or megabyte. Nothing is returned before the first 32 hardware bytes; readers wait, saying why. - A dry generator: the old key serves for at most ten minutes, then readers wait again. 32 hardware bytes that are one word eight times, or repeat the previous seed, are refused. - ChaCha20 checked against RFC 8439's test vector before first use. - A Keyring builtin waiting for a key lets go of the Dis VM. - insecurerandom keys it from the labelled counter, as before. ndb/dns read one id from #c/random at startup and counted up from it. With no generator that kept os/init/profile, and so the console shell, waiting for ever. It now reads a random id for each query, when it makes the query: the shell comes up, and lookups wait. tests/host/kernrandom_test.sh compiles random.c on the host against a simulated generator and clock: the output against a model written from the RFC, fail-closed, VM release, reseed by time and bytes, the dry window, refused seeds, insecurerandom. Each mutation tried (no key erasure, slower reseed, zero-padded seed, wrong test vector, VM held, longer dry window) fails it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0195u6z8JjsAy9yrE5TqKxzx
# Conflicts: # CLAUDE.md
# Conflicts: # .github/workflows/ci.yml # CLAUDE.md
pdfinn
marked this pull request as ready for review
November 5, 2026 08:46
This was referenced Nov 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#1051 made the rule that a secret comes only from a hardware generator. It kept the rule by reading the generator for every byte asked for. That had three problems:
/dev/randomgave 45 KB/s.This puts one deterministic generator between the hardware and every reader, as Linux, the BSDs and 9front do. It is in
os/port/random.c, so it covers every board. Readers are#c/random,#c/notquiterandom,genrandom,prng,prngtryandtruerand.The generator
genrandom.c. Each request first takes 32 bytes of keystream as the next key, and its output comes after them. The state that produced a request is gone once it returns.hwrandomalone.key' = SHA-256(32 bytes of the old stream || 32 bytes from the hardware). It reseeds every second or every megabyte, whichever comes first. Nothing is returned before the first 32 hardware bytes; readers wait, and the console says why every five seconds.insecurerandom. It keys the generator from the labelled counter, as before, and the label is now printed at boot.ndb/dns
ndb/dnsread one id from#c/randomat startup and counted up from it. With no generator, that keptos/init/profilewaiting forever, and the console shell with it; I found this on the Pi 5. #1051's run with no generator stalled the same way. dns now reads a random id for each query, at query time. The shell comes up, and lookups wait.Tests
tests/host/kernrandom_test.sh(added to CI shard 2) compilesrandom.con the host against a simulated generator and clock. It checks, in nine cases:insecurerandom;I tried these mutations, and each one fails the test: no key erasure, a slower reseed, a zero-padded short seed, a wrong test vector, the VM held while waiting, and a longer dry window.
Pi 5 board (BCM2712, RNG200)
Run on the integration branch's kernel. Its
random.canddns.bare this PR's, except for the boot-time label hunk, which does nothing on a board that has a generator.QEMU
All from this branch.
#c/randomwait and say why, nothing is made, andinsecurerandomanswers with the label.insecurerandom)Not done yet