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
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
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:
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
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
purge the old
key and replace it with something these programs control.
Seeding and Reseeding
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
AddEntropy() method occurs very early in the boot sequence before the
userspace starts. The initial seeding is required for the CPRNG to function as
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
RDSEEDinstruction 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.cprng-(re)seed-require.* options. For more information,
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.