# 随机数发生器

[English]

ESP32-C5 中包含一个硬件随机数发生器 (RNG)，可以调用 API `esp_random()` 和 `esp_fill_random()` 从中获取随机数值。

系统从随机数生成器的 RNG_DATA_REG 寄存器中读取的每一个 32 位数值都是一个真正的随机数。这些真正的随机数是基于系统中的热噪声和异步时钟失配生成的。

- 热噪声来自高速 ADC、SAR ADC 或两者。当高速 ADC 或 SAR ADC 被启用时，会生成比特流，并作为随机种子通过 XOR 逻辑门输入到随机数生成器中。

- RC_FAST_CLK 是一种异步时钟源，它通过引入电路亚稳态来增加 RNG 的熵值。详细信息请参阅 二级熵源 部分。

下图展示了 ESP32-C5 上 RNG 的噪声来源：

满足下列任意一个或多个条件时，硬件 RNG 会产生真随机数：

- RF 子系统，即 Wi-Fi 或蓝牙或 802.15.4 Thread/Zigbee 已启用。启用后，RF 子系统会在内部启用高速 ADC，该 ADC 可用作熵源。高速 ADC 可能仅在相应的 RF 子系统处于活动状态（例如未处于睡眠模式）时可用。详细信息请参阅 启用 RF 子系统 部分。

- 调用 `bootloader_random_enable()` 启用了内部熵源 (SAR ADC)，且熵源尚未被 `bootloader_random_disable()` 禁用。

- 在 二级引导加载程序 运行时。这是因为默认的 ESP-IDF 引导加载程序启动时会调用 `bootloader_random_enable()`，并在执行应用程序前调用 `bootloader_random_disable()`。

当上述任一条件为真时，物理噪声样本会连续混合到内部硬件 RNG 状态中来提供熵。如需了解详情，请参阅 ESP32-C5 技术参考手册 > 随机数发生器 (RNG) [PDF] 章节。

如果上述条件都不满足，那么 RNG 的输出仅应被看作伪随机数。

## 启用 RF 子系统

可以通过以下任一 API 来启用 RF 子系统：

- Wi-Fi: `esp_wifi_start()`

- 蓝牙 (NimBLE): `nimble_port_init()` 会在内部调用 `esp_bt_controller_enable()`

- 蓝牙 (Bluedroid): `esp_bt_controller_enable()`

- Thread/Zigbee: `esp_openthread_init()`

## 启动

在启动过程中，ESP-IDF 引导加载程序暂时会启用一个非 RF 内部熵源（SAR ADC 使用内部参考电压噪声），为首次生成的启动密钥提供熵。

但是，当应用程序开始执行后，一直到 Wi-Fi 或蓝牙或 802.15.4 Thread/Zigbee 初始化完成前或内部熵源再次被启用前，通常只有伪随机数可用。

如需在应用程序启动期间临时重启熵源，或为不使用 Wi-Fi 或蓝牙或 802.15.4 Thread/Zigbee 的应用程序临时重启熵源，请调用函数 `bootloader_random_enable()` 重启内部熵源。在使用下列任一功能前，必须调用函数 `bootloader_random_disable()` 以禁用熵源。

- ADC

- Wi-Fi 或蓝牙或 802.15.4 Thread/Zigbee

备注

ESP-IDF 二级引导加载程序在启动过程中启用的熵源会用熵来初始化内部 RNG 状态。但是，内部硬件 RNG 状态的大小并不足以提供连续的真随机数流。因此，在需要真随机数时必须启用连续的熵源。

备注

如果应用程序需要真随机数源，但无法永久性地启用硬件熵源，可以考虑使用软件 DRBG （确定性随机数发生器）来实现，如 mbedTLS CTR-DRBG 或 HMAC-DRBG，并使用来自硬件 RNG 真随机数来获取初始熵。

## 二级熵源

ESP32-C5 RNG 包含一个基于异步 8 MHz 内部振荡器采样的二级熵源（详情请参阅技术参考手册）。该熵源在 ESP-IDF 中始终处于启用状态，并通过硬件持续混合到 RNG 状态中。在测试中，即使在不启用主熵源时，这个二级熵源也足以通过 Dieharder 随机数测试套件（测试输入数据是通过连续重置 ESP32-C5 生成短样本并将其拼接来创建的）。但是，目前只有在同时启用上文所述的主熵源时，才能保证产生真随机数。

## API 参考

### Header File

- components/esp_hw_support/include/esp_random.h

- This header file can be included with:

  >     #include "esp_random.h"

### Functions

uint32_t esp_random(void)

  Get one random 32-bit word from hardware RNG.

  If Wi-Fi or Bluetooth are enabled, this function returns true random numbers. In other situations, if true random numbers are required then consult the ESP-IDF Programming Guide "Random Number Generation" section for necessary prerequisites.

  This function automatically busy-waits to ensure enough external entropy has been introduced into the hardware RNG state, before returning a new random number. This delay makes sure the reading frequency does not exceed 15-75 KHz. The actual value is dependent on the specific chip. More information on this can be found in components/esp_hw_support/hw_random.c.

  返回:

    Random value between 0 and UINT32_MAX

void esp_fill_random(void *buf, size_t len)

  Fill a buffer with random bytes from hardware RNG.

  备注

  This function is implemented via calls to esp_random(), so the same constraints apply.

  参数:

    - buf -- Pointer to buffer to fill with random numbers.

    - len -- Length of buffer in bytes

### Header File

- components/bootloader_support/include/bootloader_random.h

- This header file can be included with:

  >     #include "bootloader_random.h"

- This header file is a part of the API provided by the `bootloader_support` component. To declare that your component depends on `bootloader_support`, add the following to your CMakeLists.txt:

  >     REQUIRES bootloader_support

  > or

  >     PRIV_REQUIRES bootloader_support

### Functions

void bootloader_random_enable(void)

  Enable an entropy source for RNG if RF subsystem is disabled.

  The exact internal entropy source mechanism depends on the chip in use but all SoCs use the SAR ADC to continuously mix random bits (an internal noise reading) into the HWRNG. Consult the SoC Technical Reference Manual for more information.

  Can also be called from app code, if true random numbers are required without initialized RF subsystem. This might be the case in early startup code of the application when the RF subsystem has not started yet or if the RF subsystem should not be enabled for power saving.

  Consult ESP-IDF Programming Guide "Random Number Generation" section for details.

  警告

  This function is not safe to use if any other subsystem is accessing the RF subsystem or the ADC at the same time!

void bootloader_random_disable(void)

  Disable entropy source for RNG.

  Disables internal entropy source. Must be called after bootloader_random_enable() and before RF subsystem features, ADC, or I2S (ESP32 only) are initialized.

  Consult the ESP-IDF Programming Guide "Random Number Generation" section for details.

void bootloader_fill_random(void *buffer, size_t length)

  Fill buffer with 'length' random bytes.

  备注

  If this function is being called from app code only, and never from the bootloader, then it's better to call esp_fill_random().

  参数:

    - buffer -- Pointer to buffer

    - length -- This many bytes of random data will be copied to buffer

## `getrandom()`

为方便移植，还提供了与 Linux 的 `getrandom()` 函数兼容的版本：

    #include <sys/random.h>

    ssize_t getrandom(void *buf, size_t buflen, unsigned int flags);

此函数通过内部调用 `esp_fill_random()` 来实现。

`flags` 参数将被忽略。该函数始终是非阻塞的，但随机数的强度取决于本文档所述条件。

如果 `buf` 参数为 NULL，返回值为 -1，并将 `errno` 设置为 `EFAULT`。否则返回 `buflen`。

## `getentropy()`

为了便于移植，还提供了与 Linux 的 `getentropy()` 函数兼容的版本：

    #include <unistd.h>

    int getentropy(void *buffer, size_t length);

此函数通过内部调用 `getrandom()` 实现。

随机数强度取决于本文档所述条件。

如果执行成功则返回 0，否则返回 -1，同时：

> - 如果 `buffer` 参数为 NULL，`errno` 设置为 `EFAULT`。

> - 如果 `length` 超过 256，`errno` 设置为 `EIO`。

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。