Mode B: dice plus machine sources
Mode B combines the dice with machine entropy so that a weakness in either one alone does not sink the result.
Sources:
- the dice rolls, encoded exactly as in Mode A
/dev/urandom, read through Node'scrypto.randomBytes- the Raspberry Pi hardware RNG at
/dev/hwrng(the BCM2711iproc-rng200block), when present and healthy
These are combined with the HKDF construction specified in
packages/core/src/entropy/combine.spec.yaml:
ikm = concat over sources, in canonical order, of:
len(source_id) as one byte
source_id as ASCII
len(material) as two bytes, big endian
material
seed = HKDF-Expand(
HKDF-Extract(salt = "nullroute/entropy/v1", ikm = ikm),
info = "seed",
length = 32
)
The length prefixes are not decoration. Without them, two different source sets could concatenate to the same byte string, and the combiner would map distinct inputs to the same seed. Sources are ordered canonically by source id so the combination is deterministic regardless of the order they were collected in.
The security property is the standard HKDF extraction property: the output
retains full entropy if any single input source has full entropy. A completely
broken or adversarially chosen /dev/hwrng cannot weaken a good dice roll. That
is the whole reason to offer this mode.
The combiner does not, and cannot, detect a low-entropy source. That is the caller's job, which is why the health gates below exist.
Machine source health gates
Before any machine source is used, the daemon runs
checkEntropyHealth (packages/daemon/src/entropy/health.ts), which:
- reads
/proc/sys/kernel/random/entropy_availand refuses if the pool is clearly not initialised - confirms
/dev/hwrngexists and is readable - reads two consecutive 32-byte blocks and rejects the source if they are identical, or if either is all-zero, which is what a stuck or absent hardware RNG typically returns
- refuses machine-source modes within 60 seconds of boot if
/dev/hwrngis unavailable, because early boot is exactly when the kernel pool is weakest
Every one of those paths is Linux-only. Where a path is absent the check reports unknown, and unknown never counts as healthy: a gate that passed because it could not find its own evidence would be worse than no gate. That is why a machine-only seed cannot be generated on a development machine at all.
What the gates do not do. They catch a stuck generator, an unseeded pool and a device generating a seed in its first minute of boot. They say nothing about the quality of the numbers. A generator producing well-formed but predictable output passes all three, and that is exactly the attack rolling dice makes impossible. The screen says this beside the results, because three green ticks otherwise read as "the device checked its randomness", which is not what happened.
Mode B remains auditable, but only if you record everything. The device offers to display every source value so you can save them and reproduce the derivation later. If you do not record them, the result is not hand-reproducible, and you should understand that you have traded away the property that makes Mode A worth using.
Rendered from docs/ENTROPY.md, the same bytes that ship with the code. SHA-256 ce8ad87ef3fbce5b6ff615296b3ec081a840415ae6bc089bff748df3dc6a348d