blob: 0ec8f33d9c7283a119e8bffc8b9e7f86fdb0887a [file] [log] [blame] [view]
#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.
## Introduction
Zircon's built-in CPRNG provides cryptographically secure pseudorandom data in a
non-blocking fashion. User space programs can access them through the
[`zx_cprng_draw()`](/reference/syscalls/cprng_draw.md) syscall.
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
[`zx_cprng_add_entropy()`](/reference/syscalls/cprng_add_entropy.md)
syscall.
## Algorithms
Zircon's CPRNG is a pseudorandom number generator. Its implementation is located
at `zircon/kernel/lib/crypto`. It supports two operations, `Draw()` and
`AddEntropy()`, which corresponds to the two syscalls mentioned above. The
internal state consists of a 256-bit `key` and a 128-bit `nonce`. The `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.
When the `Draw()` method is called:
1. `nonce` is incremented.
1. The output buffer is encrypted using the ChaCha20 algorithm with `key` and
`nonce`.
Here `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>)
```
where `k<sub>old</sub>` and `k<sub>new</sub>` are the old and new `key`,
respectively, `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.
## 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
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).
## Entropy sources
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](/docs/reference/kernel/kernel_cmdline.md).
* Entropy from hardware RNG such as the `RDSEED` instruction on x86 devices and
other hardware specific RNGs.
* [Jitter Entropy](/docs/concepts/kernel/jitterentropy/README.md)
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](/docs/reference/kernel/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.