This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 2330e39546f arch/arm/rp23xx: harden the TRNG against health-test
failures
2330e39546f is described below
commit 2330e39546fed122a2fc12cf49ae18923b47dac4
Author: Ricard Rosson <[email protected]>
AuthorDate: Mon Jul 27 14:31:27 2026 +0100
arch/arm/rp23xx: harden the TRNG against health-test failures
The TRNG driver waited for EHR_VALID and copied the block out without
checking the CryptoCell health-test result, so a sample that failed the
on-chip von-Neumann, CRNGT or autocorrelation test was served as entropy.
Check RNG_ISR before consuming a block: on a health-test error, discard
the block and re-arm the source; only a block that passed is returned.
Add a small startup self-test that draws a few conditioned blocks and, if
the source is stuck (identical blocks), latches /dev/random and /dev/urandom
to fail with -EIO rather than emit predictable output that would silently
weaken every derived key.
Builds on the driver added in 52e84e0c0a.
Signed-off-by: Ricard Rosson <[email protected]>
Assisted-by: Claude (Anthropic Claude Code)
Signed-off-by: Ricard Rosson <[email protected]>
---
arch/arm/src/rp23xx/rp23xx_rng.c | 141 ++++++++++++++++++++++++++++++++++-----
1 file changed, 123 insertions(+), 18 deletions(-)
diff --git a/arch/arm/src/rp23xx/rp23xx_rng.c b/arch/arm/src/rp23xx/rp23xx_rng.c
index e2c7bca54f0..ccd5bdce857 100644
--- a/arch/arm/src/rp23xx/rp23xx_rng.c
+++ b/arch/arm/src/rp23xx/rp23xx_rng.c
@@ -28,6 +28,7 @@
#include <sys/types.h>
#include <stdint.h>
+#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
@@ -54,6 +55,20 @@
#define RP23XX_TRNG_EHR_VALID (1 << 0)
+/* RNG_ISR health-test error bits: a sample that failed the on-chip
+ * von-Neumann, CRNGT or autocorrelation test must be discarded.
+ */
+
+#define RP23XX_TRNG_ISR_ERRORS (RP23XX_TRNG_RNG_ISR_VN_ERR | \
+ RP23XX_TRNG_RNG_ISR_CRNGT_ERR | \
+ RP23XX_TRNG_RNG_ISR_AUTOCORR_ERR)
+
+/* Startup self-test: draw this many blocks and reject an obviously broken
+ * source (stuck or all-constant) before the RNG is trusted.
+ */
+
+#define RP23XX_TRNG_SELFTEST_BLOCKS 4
+
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
@@ -66,6 +81,8 @@ static ssize_t rp23xx_rng_read(struct file *filep, char
*buffer,
****************************************************************************/
static mutex_t g_rng_lock = NXMUTEX_INITIALIZER;
+static bool g_rng_healthy; /* startup self-test passed */
+static bool g_rng_inited;
static const struct file_operations g_rngops =
{
@@ -81,13 +98,90 @@ static const struct file_operations g_rngops =
* Private Functions
****************************************************************************/
+/****************************************************************************
+ * Name: rp23xx_rng_collect
+ *
+ * Description:
+ * Wait for one fresh 192-bit block into 'ehr', discarding any block the
+ * on-chip health tests rejected (re-arming the source each time). The
+ * entropy source must already be enabled.
+ *
+ ****************************************************************************/
+
+static void rp23xx_rng_collect(uint32_t ehr[RP23XX_TRNG_EHR_WORDS])
+{
+ int i;
+
+ for (; ; )
+ {
+ uint32_t isr = getreg32(RP23XX_TRNG_RNG_ISR);
+
+ if ((isr & RP23XX_TRNG_ISR_ERRORS) != 0)
+ {
+ /* Health test failed: drop the block and restart the source. */
+
+ putreg32(0xffffffff, RP23XX_TRNG_RNG_ICR);
+ putreg32(0, RP23XX_TRNG_RND_SOURCE_ENABLE);
+ putreg32(1, RP23XX_TRNG_RND_SOURCE_ENABLE);
+ continue;
+ }
+
+ if ((isr & RP23XX_TRNG_RNG_ISR_EHR_VALID) != 0)
+ {
+ break;
+ }
+ }
+
+ for (i = 0; i < RP23XX_TRNG_EHR_WORDS; i++)
+ {
+ ehr[i] = getreg32(RP23XX_TRNG_EHR_DATA(i));
+ }
+
+ putreg32(0xffffffff, RP23XX_TRNG_RNG_ICR);
+}
+
+/****************************************************************************
+ * Name: rp23xx_rng_selftest
+ *
+ * Description:
+ * Draw a few conditioned blocks at startup and reject an obviously broken
+ * source (a stuck source emitting identical blocks). The on-chip tests
+ * cover statistical quality; this only catches a dead source before any
+ * key is derived from it. Returns true if the source looks healthy.
+ *
+ ****************************************************************************/
+
+static bool rp23xx_rng_selftest(void)
+{
+ uint32_t block[RP23XX_TRNG_SELFTEST_BLOCKS][RP23XX_TRNG_EHR_WORDS];
+ bool ok = true;
+ int i;
+
+ putreg32(1, RP23XX_TRNG_RND_SOURCE_ENABLE);
+
+ for (i = 0; i < RP23XX_TRNG_SELFTEST_BLOCKS; i++)
+ {
+ rp23xx_rng_collect(block[i]);
+
+ if (i > 0 && memcmp(block[i], block[i - 1],
+ RP23XX_TRNG_EHR_BYTES) == 0)
+ {
+ ok = false;
+ break;
+ }
+ }
+
+ putreg32(0, RP23XX_TRNG_RND_SOURCE_ENABLE);
+ return ok;
+}
+
/****************************************************************************
* Name: rp23xx_rng_read
*
* Description:
- * Fill 'buffer' with 'buflen' bytes of hardware entropy. Enables the ring
- * -oscillator source, then repeatedly waits for a valid 192-bit EHR and
- * copies it out until the request is satisfied.
+ * Fill 'buffer' with 'buflen' bytes of hardware entropy, discarding any
+ * block the on-chip health tests reject. Fails with -EIO if the startup
+ * self-test found the source broken.
*
****************************************************************************/
@@ -103,6 +197,12 @@ static ssize_t rp23xx_rng_read(struct file *filep, char
*buffer,
return ret;
}
+ if (!g_rng_healthy)
+ {
+ nxmutex_unlock(&g_rng_lock);
+ return -EIO;
+ }
+
/* Enable the entropy source. */
putreg32(1, RP23XX_TRNG_RND_SOURCE_ENABLE);
@@ -111,22 +211,8 @@ static ssize_t rp23xx_rng_read(struct file *filep, char
*buffer,
{
uint32_t ehr[RP23XX_TRNG_EHR_WORDS];
size_t chunk;
- int i;
-
- /* Wait for the next 192-bit sample to become valid. */
-
- while ((getreg32(RP23XX_TRNG_TRNG_VALID) & RP23XX_TRNG_EHR_VALID) == 0)
- {
- }
-
- for (i = 0; i < RP23XX_TRNG_EHR_WORDS; i++)
- {
- ehr[i] = getreg32(RP23XX_TRNG_EHR_DATA(i));
- }
- /* Acknowledge the sample so the engine collects the next one. */
-
- putreg32(0xffffffff, RP23XX_TRNG_RNG_ICR);
+ rp23xx_rng_collect(ehr);
chunk = buflen - nread;
if (chunk > RP23XX_TRNG_EHR_BYTES)
@@ -146,6 +232,23 @@ static ssize_t rp23xx_rng_read(struct file *filep, char
*buffer,
return (ssize_t)nread;
}
+/****************************************************************************
+ * Name: rp23xx_rng_init
+ *
+ * Description:
+ * Run the startup self-test once and latch the result. Idempotent.
+ *
+ ****************************************************************************/
+
+static void rp23xx_rng_init(void)
+{
+ if (!g_rng_inited)
+ {
+ g_rng_healthy = rp23xx_rng_selftest();
+ g_rng_inited = true;
+ }
+}
+
/****************************************************************************
* Public Functions
****************************************************************************/
@@ -162,6 +265,7 @@ static ssize_t rp23xx_rng_read(struct file *filep, char
*buffer,
#ifdef CONFIG_DEV_RANDOM
void devrandom_register(void)
{
+ rp23xx_rng_init();
register_driver("/dev/random", &g_rngops, 0444, NULL);
}
#endif
@@ -178,6 +282,7 @@ void devrandom_register(void)
#ifdef CONFIG_DEV_URANDOM_ARCH
void devurandom_register(void)
{
+ rp23xx_rng_init();
register_driver("/dev/urandom", &g_rngops, 0444, NULL);
}
#endif