#Cryptographically Secure Pseudo Random Number Generator
This document describes the design of Zircon's Cryptographically secure pseudo random number generator (CPRNG), including its algorithm, (re)seeding process, and entropy sources.
Zircon's built-in CPRNG provides cryptographically secure pseudorandom data in a non-blocking fashion. User space programs can access them through the
Zircon's CPRNG only trusts entropy sources directly accessible from within the kernel because anything outside the kernel such as the drivers, which are considered userspace programs, cannot be trusted. For the CPRNG to function properly and securely, at least one of these sources is required. However, userspace programs may inject additional entropy to CPRNG through the
Zircon's CPRNG is a pseudorandom number generator. Its implementation is located at
zircon/kernel/lib/crypto. It supports two operations,
AddEntropy(), which corresponds to the two syscalls mentioned above. The internal state consists of a 256-bit
key and a 128-bit
key must be kept secret because the CPRNG output can be reliably predicted with the knowledge of it. At the beginning,
key is initialized with some random bytes (see next section) and
nonce is initialized as 0.
Draw() method is called:
nonce is incremented.
The output buffer is encrypted using the ChaCha20 algorithm with
nonce is incremented for every
Draw() request to ensure different results. The caller provides a buffer to perform the encryption in-place. Any existing data in the buffer is used since they does not affect the security properties.
When there is a
AddEntropy() request, the
key is updated by mixing additional entropy with the old key:
k<sub>new</sub> = H(e || k<sub>old</sub>)
k<sub>new</sub> are the old and new
e is the input bytes,
H is the SHA256 hash function and
|| denotes concatenation. The old key is included in the hash to ensure that callers, e.g. userspace programs that call
zx_cprng_add_entropy(), cannot purge the old
key and replace it with something these programs control.
A call to the
AddEntropy() method performs the initial seeding of the Zircon CPRNG. The initial seeding is needed for virtual memory ASLR, so the first call to the
AddEntropy() method occurs very early in the boot sequence before the userspace starts. The initial seeding is required for the CPRNG to function as the
Draw() method blocks until enough entropy is added.
After the initial seeding, a thread is created to reseed the CPRNG every 30 seconds by calling the
AddEntropy() method. This ensures forward secrecy (a guarantee of secrecy for all the CPRNG's previous output since the last reseed, even if its internal state is compromised).
There are several entropy sources Zircon's CPRNG can utilize for seeding and reseeding:
Entropy from kernel cmdline option
kernel.entropy-mixin, documented in kernel_cmdline.md.
Entropy from hardware RNG such as the
RDSEED instruction on x86 devices and other hardware specific RNGs.
The kernel cmdline is only used at initial seeding because it is a constant passed in at boot for one-time use only. The entropy from hardware and jitter entropy can be used for both initial seeding and reseeding. To ensure the CPRNG is sufficiently (re)seeded from the selected entropy sources, you can use the kernel cmdline
kernel.cprng-(re)seed-require.* options. For more information, see kernel_cmdline.md.
There may be other available entropy sources such as a trusted platform module (TPM), but we do not currently have a strong framework in place for userspace programs to securely communicate with the CPRNG subsystem in the kernel.