Hardware entropy across the supported boards
How randomness reaches the SeedSigner app differs per platform, and the differences are not obvious from the board configs alone. This page records the current state, why each board is configured the way it is, and how to verify it on a running device.
How entropy reaches the app
The app never reads a hardware RNG directly. Every path — seed generation, camera entropy, PIN salts — goes through os.urandom(), i.e. the kernel CSPRNG (getrandom(2)). This is correct and should not change: the kernel CSPRNG is the right thing to draw key material from.
The question this page answers is a different one: does the hardware RNG actually reach that kernel pool? A CSPRNG produces statistically perfect output whether or not a hardware source is feeding it, so this cannot be established by looking at /dev/urandom.
There are two mechanisms that move bytes from /dev/hwrng into the kernel pool, and a board needs at least one of them:
khwrngd, the in-kernel filler thread.drivers/char/hw_random/core.cstarts it only when the registered driver reports a non-zeroquality. It then credits entropy atquality/1024bits per bit. Most drivers set no quality at all, in which case this thread never runs.rngd, from therng-toolspackage. A userspace daemon that reads/dev/hwrng, runs FIPS 140-2 continuous tests on it, and injects the result with an entropy credit viaRNDADDENTROPY. Installed as/etc/init.d/S21rngd.
Current state per board
| Board | RNG driver | Driver quality | khwrngd runs? | rngd | Hardware entropy reaches pool |
|---|---|---|---|---|---|
| Luckfox Pico (RV1103/RV1106) | rockchip-rng (rockchip,trngv1) | 999 | yes | yes | via both |
| Pi — smartcard builds | bcm2835-rng / iproc-rng200 | 0 | no | yes | via rngd |
| Pi — plain builds | bcm2835-rng / iproc-rng200 | 0 | no | yes (added — see below) | via rngd |
| La Frite (smartcard only) | meson-rng | not audited | — | yes | via rngd |
Luckfox Pico
The TRNG on RV1103/RV1106 is TRNG v1, a separate IP block (rng@ff448000, its own HCLK_TRNG_NS clock) — not the RNG that lived inside the crypto block on crypto v1/v2 hardware. Enabling &crypto therefore does not give you /dev/hwrng; the two are independent.
rv1106.dtsi ships &rng with status = "disabled", and it is only enabled because upstream rv1106-evb.dtsi — pulled in by every Luckfox board .dts — turns it on. Because that is an uncontrolled upstream dependency, opt/luckfox/os-build.sh pins &rng to okay itself (apply_rng_dts_patch → enable_dts_node) and fails the build if it cannot be verified, rather than inheriting the setting by luck. An SDK bump can no longer silently remove the entropy source. (The &crypto node is intentionally left alone — see the hardware-crypto note below.)
rockchip-rng.c sets quality = 999, so khwrngd runs and credits roughly 7.8 bits per byte. rng-tools is also installed, giving a second, independently tested path.
Note:
BR2_PACKAGE_URANDOM_SCRIPTS=yis selected, but the Luckfox rootfs is a read-only squashfs with tmpfs overlays, so the saved seed does not survive a power cycle. Every boot starts from a cold pool and depends on the TRNG. Combined with there being no RTC, this makes the TRNG load-bearing on this platform rather than a nice-to-have.
Raspberry Pi
bcm2835-rng (Pi 0/02W/2) and iproc-rng200 (Pi 4) set no .quality, and the hwrng core’s default_quality is 0. khwrngd therefore never starts, and a userspace read of /dev/hwrng credits nothing. On the Pi, rngd is the only mechanism that moves hardware entropy into the pool.
The smartcard builds have always had BR2_PACKAGE_RNG_TOOLS=y. The plain builds did not, which meant /dev/hwrng existed but nothing ever read it and the hardware RNG contributed zero bits. BR2_PACKAGE_RNG_TOOLS=y has been added to the eight plain board defconfigs (pi0, pi02w, pi2, pi4 and their -dev variants) so every Pi image behaves the same way.
Two related notes:
opt/pi0/board/kernel.confighadCONFIG_HW_RANDOMcommented out where every other Pi board set it explicitly. BecauseBR2_LINUX_KERNEL_USE_CUSTOM_CONFIGmakes that file the whole.configandCONFIG_MODULESis off,olddefconfigpromoted the Kconfigdefault mtoyand the driver was built anyway — correct, but by accident. It is now set explicitly.- Every Pi and La Frite board removes
/etc/init.d/S20seedrnginpost-build.sh, so no entropy seed is carried across reboots on any platform.rngdrefills the pool at boot instead.
Verifying on a device
Use a dev image — the non-dev UART has no shell.
# 1. Which RNG is registered? Expect "rockchip" on Luckfox, "bcm2835-rng" on Pi.
cat /sys/devices/virtual/misc/hw_random/rng_current
# 2. Does the device produce different bytes each read?
cat /dev/hwrng | od -x | head -n 1
# 3. Is the in-kernel filler thread running? (Luckfox: yes. Pi: no, by design.)
ps | grep '[h]wrng'
# 4. Is rngd running? This is the load-bearing one on Pi.
pgrep rngd
# 5. Rockchip hardware crypto algorithms (Luckfox) — currently returns NOTHING; see note below
cat /proc/crypto | grep rk
If step 4 fails on a Pi image, the hardware RNG is contributing nothing — that is the exact regression the rng-tools addition prevents.
Bench-confirmed on a SeedSigner Luckfox Pico Mini NAND build (RV1103, dev, 2026-09): rng_current reads rockchip, the in-kernel [hwrng] filler thread is running (pid 41), /dev/hwrng returns fresh bytes each read, the boot log shows random: crng init done, “Seeding 256 bits and crediting” and Starting rngd — so the TRNG path is fully working, both via the kthread and rngd. This is the entropy guarantee this branch was about, and it holds on real hardware.
The hardware crypto engine is deliberately NOT enabled (step 5 above returns nothing — it is informational). SeedSigner uses software crypto and never touches the engine. An earlier build forced the umbrella
CONFIG_CRYPTO_DEV_ROCKCHIP=yand pinned&crypto, but that never produced a working driver: on RV1106 the algorithm code is gated behindCONFIG_CRYPTO_DEV_ROCKCHIP_V3, so nothing compiled — confirmed on a flashed board (/proc/cryptohas norkentries,/sys/bus/platform/drivers/has no crypto driver,ff440000.cryptois unbound). The build’s assertion only grepped the defconfig text, so it printed “enabled” regardless. That dead enable and the&cryptoDTS pin have been removed — the build now enables only the TRNG (&rng+CONFIG_HW_RANDOM_ROCKCHIP), which is what actually works. Re-adding hardware crypto would needCONFIG_CRYPTO_DEV_ROCKCHIP_V3=yand a post-build check against the real.config//proc/crypto.
Known limitation: the app’s RNG health monitor
HardwareRngMonitorThread in the app repo (src/seedsigner/hardware/rng_monitor.py) samples os.urandom() and runs statistical health checks on it — Shannon entropy, stuck-sample and short-cycle detection.
Because os.urandom() is CSPRNG output, those checks cannot fail even if the hardware RNG has been dead since boot. The monitor is a useful guard against a broken getrandom(), but despite its name it says nothing about the TRNG. Detecting a stuck or failed hardware RNG requires reading /dev/hwrng directly, which is what rngd’s FIPS tests already do on every board.
Any future change to sample /dev/hwrng from the app must account for the platform differences above: on the Pi the device node exists and is readable even in the failure mode where nothing is feeding the kernel pool, so merely opening it is not a sufficient health check.
See also
docs/luckfox/secure-boot.md— OTP, secure boot, and the RV1106 crypto block- Rockchip Crypto/HWRNG Developer Guide V1.2.1, §2.2 (HWRNG) and §2.2.4 (verification)