[core] Use SplitMix32 PRNG for random_uint32() (#14984)

This commit is contained in:
J. Nick Koston
2026-03-20 15:25:54 -10:00
committed by GitHub
parent f3cddcee21
commit 95dea59382
8 changed files with 34 additions and 23 deletions

View File

@@ -14,7 +14,6 @@
namespace esphome {
uint32_t random_uint32() { return esp_random(); }
bool random_bytes(uint8_t *data, size_t len) {
esp_fill_random(data, len);
return true;

View File

@@ -8,8 +8,6 @@
#include <sys/ioctl.h>
#endif
#include <unistd.h>
#include <limits>
#include <random>
#include "esphome/core/defines.h"
#include "esphome/core/log.h"
@@ -18,13 +16,6 @@ namespace esphome {
static const char *const TAG = "helpers.host";
uint32_t random_uint32() {
std::random_device dev;
std::mt19937 rng(dev());
std::uniform_int_distribution<uint32_t> dist(0, std::numeric_limits<uint32_t>::max());
return dist(rng);
}
bool random_bytes(uint8_t *data, size_t len) {
FILE *fp = fopen("/dev/urandom", "r");
if (fp == nullptr) {

View File

@@ -8,8 +8,6 @@
namespace esphome {
uint32_t random_uint32() { return rand(); }
bool random_bytes(uint8_t *data, size_t len) {
lt_rand_bytes(data, len);
return true;

View File

@@ -17,15 +17,6 @@
namespace esphome {
uint32_t random_uint32() {
uint32_t result = 0;
for (uint8_t i = 0; i < 32; i++) {
result <<= 1;
result |= rosc_hw->randombit;
}
return result;
}
bool random_bytes(uint8_t *data, size_t len) {
while (len-- != 0) {
uint8_t result = 0;

View File

@@ -122,6 +122,8 @@ def zephyr_to_code(config: ConfigType) -> None:
zephyr_add_prj_conf("FPU", True)
zephyr_add_prj_conf("NEWLIB_LIBC_FLOAT_PRINTF", True)
zephyr_add_prj_conf("STD_CPP20", True)
# random_bytes() uses sys_rand_get() which requires the entropy subsystem
zephyr_add_prj_conf("ENTROPY_GENERATOR", True)
# <err> os: ***** USAGE FAULT *****
# <err> os: Illegal load of EXC_RETURN into PC

View File

@@ -75,7 +75,6 @@ IRAM_ATTR InterruptLock::~InterruptLock() { irq_unlock(state_); }
// Zephyr LwIPLock is defined inline as a no-op in helpers.h
uint32_t random_uint32() { return rand(); } // NOLINT(cert-msc30-c, cert-msc50-cpp)
bool random_bytes(uint8_t *data, size_t len) {
sys_rand_get(data, len);
return true;

View File

@@ -156,6 +156,32 @@ uint32_t fnv1_hash(const char *str) {
return hash;
}
// SplitMix32 — a fast, non-cryptographic PRNG from the SplitMix family
// (Steele et al., 2014). Uses a Weyl sequence with golden-ratio increment
// and the MurmurHash3 32-bit finalizer as output mixing function.
// Reference: https://doi.org/10.1145/2714064.2660195
// Test results: https://lemire.me/blog/2017/08/22/testing-non-cryptographic-random-number-generators-my-results/
// Seeded lazily from the platform's secure RNG via random_bytes().
// ESP8266 uses os_random() instead (defined in esp8266/helpers.cpp).
#ifndef USE_ESP8266
static uint32_t splitmix32_state; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
uint32_t random_uint32() {
// State of 0 means unseeded. The state will wrap back to 0 after 2^32 calls,
// triggering one extra random_bytes() call — an acceptable trade-off vs. adding
// a separate bool flag (4 bytes BSS + branch on every call).
if (splitmix32_state == 0) {
random_bytes(reinterpret_cast<uint8_t *>(&splitmix32_state), sizeof(splitmix32_state));
splitmix32_state |= 1; // ensure non-zero seed
}
splitmix32_state += 0x9e3779b9u;
uint32_t z = splitmix32_state;
z = (z ^ (z >> 16)) * 0x85ebca6bu;
z = (z ^ (z >> 13)) * 0xc2b2ae35u;
return z ^ (z >> 16);
}
#endif
float random_float() { return static_cast<float>(random_uint32()) / static_cast<float>(UINT32_MAX); }
// Strings

View File

@@ -842,10 +842,15 @@ template<typename ReturnT = uint32_t> inline constexpr ESPHOME_ALWAYS_INLINE Ret
}
/// Return a random 32-bit unsigned integer.
/// Not thread-safe. Must only be called from the main loop.
/// Not suitable for cryptographic use; use random_bytes() instead.
uint32_t random_uint32();
/// Return a random float between 0 and 1.
/// Not thread-safe. Must only be called from the main loop.
/// Not suitable for cryptographic use; use random_bytes() instead.
float random_float();
/// Generate \p len number of random bytes.
/// Generate \p len random bytes using the platform's secure RNG (hardware RNG or OS CSPRNG).
/// Thread-safe. Suitable for cryptographic use.
bool random_bytes(uint8_t *data, size_t len);
///@}