| #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. |
| |