Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 18 additions & 21 deletions libraries/Bluefruit54Lib/src/bluefruit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
57 changes: 39 additions & 18 deletions libraries/nRF54Crypto/src/nRF54Crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down
Loading