Raspberry Pi 3B Tinkering Notes: Hardware Random Number Generator

This post is automatically translated with LLM. The translation content has NOT been reviewed and may contain errors.

Random numbers play a crucial role in computing. For example, commonly used SSL encryption algorithms heavily rely on random numbers. If the random numbers aren't sufficiently random, attackers might potentially guess them, causing the entire encryption verification system to collapse. However, due to the deterministic nature of computers (where zero is strictly zero and one is strictly one), they cannot generate truly random numbers and can only simulate randomness through complex algorithms.

On Linux systems, leveraging its "everything is a file" philosophy, random numbers generated by the Linux kernel from aggregated system data can be read from /dev/random. But because Linux prioritizes security and collects extensive data, the random number generation speed is very slow. This can be observed using the rngtest tool from the rng-tools package:

lantian@lantian-rpi3:~ $ cat /dev/random | rngtest -c 1000
rngtest 2-unofficial-mt.14
Copyright (c) 2004 by Henrique de Moraes Holschuh
This is free software; see the source for copying conditions.  There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

rngtest: starting FIPS tests...
rngtest: bits received from input: 20000032
rngtest: FIPS 140-2 successes: 999
rngtest: FIPS 140-2 failures: 1
rngtest: FIPS 140-2(2001-10-10) Monobit: 0
rngtest: FIPS 140-2(2001-10-10) Poker: 1
rngtest: FIPS 140-2(2001-10-10) Runs: 0
rngtest: FIPS 140-2(2001-10-10) Long run: 0
rngtest: FIPS 140-2(2001-10-10) Continuous run: 0
rngtest: input channel speed: (min=167.862; avg=361.389; max=4358.681)Kibits/s
rngtest: FIPS tests speed: (min=2.087; avg=13.116; max=14.309)Mibits/s
rngtest: Program run time: 55507560 microseconds

The read speed of /dev/random is only 361.389 Kbit/s (note: not kilobytes per second). In scenarios requiring large volumes of random numbers, programs are forced to wait for Linux to generate more, causing significant delays and stuttering.

However, we often don't need such highly random numbers. The Linux kernel also provides /dev/urandom, which uses a simpler algorithm and offers tens of thousands of times faster performance compared to /dev/random:

lantian@lantian-rpi3:~ $ cat /dev/urandom | rngtest -c 1000
rngtest 2-unofficial-mt.14
Copyright (c) 2004 by Henrique de Moraes Holschuh
This is free software; see the source for copying conditions.  There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

rngtest: starting FIPS tests...
rngtest: bits received from input: 20000032
rngtest: FIPS 140-2 successes: 1000
rngtest: FIPS 140-2 failures: 0
rngtest: FIPS 140-2(2001-10-10) Monobit: 0
rngtest: FIPS 140-2(2001-10-10) Poker: 0
rngtest: FIPS 140-2(2001-10-10) Runs: 0
rngtest: FIPS 140-2(2001-10-10) Long run: 0
rngtest: FIPS 140-2(2001-10-10) Continuous run: 0
rngtest: input channel speed: (min=334.623; avg=2492.940; max=9536.743)Mibits/s
rngtest: FIPS tests speed: (min=5.508; avg=12.871; max=14.245)Mibits/s
rngtest: Program run time: 1492273 microseconds

The speed reaches 2492.940 Mbit/s—orders of magnitude faster than /dev/random. But these random numbers aren't sufficiently random and could potentially be guessed by attackers.

If software solutions can't resolve this issue, hardware can step in. Modern motherboards typically include built-in hardware random number generators (HRNGs), which generate random numbers using electrical signals from the motherboard. Due to the high volume of data traffic, these electrical signals are generally unpredictable, making hardware-generated random numbers considered secure.

The Broadcom chipset in Raspberry Pi 3B also includes a hardware random number generator, accessible at /dev/hwrng. Its performance is as follows:

lantian@lantian-rpi3:~ $ cat /dev/hwrng | rngtest -c 1000
rngtest 2-unofficial-mt.14
Copyright (c) 2004 by Henrique de Moraes Holschuh
This is free software; see the source for copying conditions.  There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

rngtest: starting FIPS tests...
rngtest: bits received from input: 20000032
rngtest: FIPS 140-2 successes: 999
rngtest: FIPS 140-2 failures: 1
rngtest: FIPS 140-2(2001-10-10) Monobit: 0
rngtest: FIPS 140-2(2001-10-10) Poker: 0
rngtest: FIPS 140-2(2001-10-10) Runs: 0
rngtest: FIPS 140-2(2001-10-10) Long run: 1
rngtest: FIPS 140-2(2001-10-10) Continuous run: 0
rngtest: input channel speed: (min=18.251; avg=1103.150; max=9765625.000)Kibits/s
rngtest: FIPS tests speed: (min=2.416; avg=13.004; max=14.341)Mibits/s
rngtest: Program run time: 20213577 microseconds

At 1103.150 Kbit/s, it's three times faster than /dev/random, significantly boosting performance for random-number-dependent applications. However, most programs only recognize /dev/random and ignore hardware RNGs. How to address this?

As mentioned earlier, /dev/random is slow. To mitigate this, Linux employs a "randomness pool": when random numbers aren't needed, Linux gradually collects entropy from system activities in the background and stores it in the /dev/random pool. During sudden high demand, it draws from this pool to handle bursts without stuttering. Thus, we can feed random numbers from /dev/hwrng into the /dev/random pool. The necessary tools are in the rng-tools package:

sudo apt-get install rng-tools
sudo nano /etc/default/rng-tools
# 添加以下内容
HRNGDEVICE=/dev/hwrng
RNGDOPTIONS="--fill-watermark=50% --feed-interval=1"
# 保存,最后运行以下命令
sudo service rng-tools restart

The HRNGDEVICE line specifies the source of supplemental randomness—here we point it to the hardware RNG. Some tutorials suggest setting this to /dev/urandom, but as noted earlier, urandom isn't secure enough and would compromise system security.

In RNGDOPTIONS, --fill-watermark replenishes the pool to the specified capacity (here 50%) when entropy is low. We avoid 100% because hardware RNG implementations vary across vendors and are typically undisclosed, raising concerns about potential hidden backdoors. Filling to 100% implies full trust in the hardware RNG, which could pose security risks.

--feed-interval sets the interval for slow replenishment after reaching the target capacity; 1 is generally sufficient.

After this configuration, /dev/random's random number generation efficiency will improve dramatically.