diff --git a/libraries/Bluefruit54Lib/src/bluefruit.cpp b/libraries/Bluefruit54Lib/src/bluefruit.cpp index 182abad..3b05fcc 100644 --- a/libraries/Bluefruit54Lib/src/bluefruit.cpp +++ b/libraries/Bluefruit54Lib/src/bluefruit.cpp @@ -84,6 +84,14 @@ static void bluefruit_blinky_cb( TimerHandle_t xTimer ) #endif } +// s145 has no entropy source of its own and asks the application for a seed (NRF_EVT_RAND_SEED_REQUEST). +// The RNG is left running: LESC key generation draws from it on another task. +static bool seed_softdevice_rng(void) +{ + uint8_t seed[SD_RAND_SEED_SIZE]; + return nRF54Crypto.begin() && nRF54Crypto.random(seed, sizeof(seed)) && (sd_rand_seed_set(seed) == NRF_SUCCESS); +} + static void nrf_error_cb(uint32_t id, uint32_t pc, uint32_t info) { #if CFG_DEBUG @@ -288,15 +296,8 @@ bool AdafruitBluefruit::begin(uint8_t prph_count, uint8_t central_count) if ( sd_err != NRF_SUCCESS ) sd_isr_forwarding_disable(); VERIFY_STATUS( sd_err, false ); - // s145 asks for a seed (NRF_EVT_RAND_SEED_REQUEST) and sd_ble_enable() fails with INVALID_STATE without one - { - uint8_t seed[SD_RAND_SEED_SIZE]; - nRF54Crypto.begin(); - bool seeded = nRF54Crypto.random(seed, sizeof(seed)); - nRF54Crypto.end(); - VERIFY(seeded, false); - VERIFY_STATUS( sd_rand_seed_set(seed), false ); - } + // sd_ble_enable() fails with INVALID_STATE until the RNG is seeded + VERIFY( seed_softdevice_rng(), false ); /*------------------------------------------------------------------*/ /* SoftDevice Default Configuration depending on the number of @@ -672,9 +673,15 @@ void adafruit_soc_task(void* arg) { (void) arg; + // An unanswered seed request leaves the SoftDevice RNG unseeded, so a failed seed is retried between + // event batches rather than inline, where it would hold up flash completions. + bool seed_pending = false; + while (1) { - if ( xSemaphoreTake(Bluefruit._soc_event_sem, portMAX_DELAY) ) + if ( seed_pending ) seed_pending = !seed_softdevice_rng(); + + if ( xSemaphoreTake(Bluefruit._soc_event_sem, seed_pending ? pdMS_TO_TICKS(10) : portMAX_DELAY) ) { uint32_t soc_evt; uint32_t err = ERROR_NONE; @@ -694,17 +701,7 @@ void adafruit_soc_task(void* arg) break; case NRF_EVT_RAND_SEED_REQUEST: - { - // S145 requires the application to seed the RNG - uint8_t seed[SD_RAND_SEED_SIZE]; - // Use FICR device ID and GRTC counter as entropy source - uint32_t* seed32 = (uint32_t*)seed; - for (uint32_t i = 0; i < SD_RAND_SEED_SIZE / 4; i++) - { - seed32[i] = NRF_FICR->INFO.DEVICEID[i & 1] ^ (uint32_t)(NRF_GRTC->SYSCOUNTER[0].SYSCOUNTERL + i); - } - sd_rand_seed_set(seed); - } + seed_pending = true; break; default: break; diff --git a/libraries/nRF54Crypto/src/nRF54Crypto.cpp b/libraries/nRF54Crypto/src/nRF54Crypto.cpp index 64a6a8d..8218f83 100644 --- a/libraries/nRF54Crypto/src/nRF54Crypto.cpp +++ b/libraries/nRF54Crypto/src/nRF54Crypto.cpp @@ -45,18 +45,32 @@ nRF54CryptoClass nRF54Crypto; static bool _rng_started = false; +// Enable with 1x 128-bit block for AES conditioning +static void cracen_rng_control(bool soft_reset) +{ + nrf_cracen_rng_control_t cfg = { 0 }; + cfg.enable = true; + cfg.number_128_blocks = 1; + cfg.soft_reset = soft_reset; + nrf_cracen_rng_control_set(NRF_CRACENCORE, &cfg); +} + +// A halted generator needs a soft reset before it produces again. Done in place rather than through +// stop/start, so a reader on another task never finds the RNG disabled under it. +static void cracen_rng_reset_if_halted(void) +{ + if (nrf_cracen_rng_fsm_state_get(NRF_CRACENCORE) != NRF_CRACEN_RNG_FSM_STATE_ERROR) return; + cracen_rng_control(true); + cracen_rng_control(false); +} + static bool cracen_rng_start(void) { if (_rng_started) return true; // Enable CRACEN RNG module nrf_cracen_module_enable(NRF_CRACEN, NRF_CRACEN_MODULE_RNG_MASK); - - // Configure RNG: enable with 1x 128-bit block for AES conditioning - nrf_cracen_rng_control_t cfg = { 0 }; - cfg.enable = true; - cfg.number_128_blocks = 1; - nrf_cracen_rng_control_set(NRF_CRACENCORE, &cfg); + cracen_rng_control(false); // Wait for the FSM to leave RESET/STARTUP; a full FIFO parks it in IDLE_STANDBY (rings off) uint32_t timeout = RNG_TIMEOUT; @@ -68,13 +82,7 @@ static bool cracen_rng_start(void) _rng_started = true; return true; } - if (state == NRF_CRACEN_RNG_FSM_STATE_ERROR) { - // A halted generator needs a soft reset before it restarts - cfg.soft_reset = true; - nrf_cracen_rng_control_set(NRF_CRACENCORE, &cfg); - cfg.soft_reset = false; - nrf_cracen_rng_control_set(NRF_CRACENCORE, &cfg); - } + cracen_rng_reset_if_halted(); } return false; } @@ -92,21 +100,34 @@ static void cracen_rng_stop(void) _rng_started = false; } +// Readers run on different tasks (SoftDevice seed requests, LESC key generation). Another reader, or +// a reset clearing the FIFO, between the level check and the read would hand out a word that is not +// random, so the two happen in one critical section. Waiting for data stays outside it. +static bool cracen_rng_word(uint32_t *word) +{ + taskENTER_CRITICAL(); + bool got = nrf_cracen_rng_fifo_level_get(NRF_CRACENCORE) > 0; + if (got) { + *word = nrf_cracen_rng_fifo_get(NRF_CRACENCORE); + } else { + cracen_rng_reset_if_halted(); + } + taskEXIT_CRITICAL(); + return got; +} + static bool cracen_rng_fill(uint8_t *dest, size_t len) { if (!_rng_started) return false; size_t offset = 0; while (offset < len) { - // Wait for FIFO to have data + uint32_t word; uint32_t timeout = RNG_TIMEOUT; - while (nrf_cracen_rng_fifo_level_get(NRF_CRACENCORE) == 0) { + while (!cracen_rng_word(&word)) { if (--timeout == 0) return false; } - // Read a 32-bit random word - uint32_t word = nrf_cracen_rng_fifo_get(NRF_CRACENCORE); - // Copy bytes (handle partial word at end) size_t remaining = len - offset; size_t to_copy = (remaining < 4) ? remaining : 4;