Files
ESP32_Serial_Swiss_Army_Knife/src/secure_random.c
T

132 lines
3.7 KiB
C

/* SPDX-License-Identifier: GPL-3.0-only */
/* Pre-radio seeding and serialized access to the device-wide CTR_DRBG. */
#include "secure_random.h"
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include "bootloader_random.h"
#include "esp_random.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "mbedtls/ctr_drbg.h"
static SemaphoreHandle_t s_random_mutex;
static mbedtls_ctr_drbg_context s_drbg;
static bool s_drbg_ready;
static uint32_t s_generate_calls;
static int pre_radio_entropy(void *context, unsigned char *output, size_t length)
{
(void)context;
/* This callback is deliberately reachable only from secure_random_init(). */
bootloader_random_enable();
esp_fill_random(output, length);
bootloader_random_disable();
return 0;
}
esp_err_t secure_random_init(void)
{
static const unsigned char personalization[] =
"esp32-serial-swiss-army-knife";
if (s_drbg_ready) {
return ESP_OK;
}
if (s_random_mutex == NULL) {
s_random_mutex = xSemaphoreCreateMutex();
if (s_random_mutex == NULL) {
return ESP_ERR_NO_MEM;
}
}
xSemaphoreTake(s_random_mutex, portMAX_DELAY);
esp_err_t error = ESP_OK;
if (!s_drbg_ready) {
mbedtls_ctr_drbg_init(&s_drbg);
int result = mbedtls_ctr_drbg_seed(&s_drbg,
pre_radio_entropy,
NULL,
personalization,
sizeof(personalization) - 1U);
if (result == 0) {
/* Reseeding would re-enter a pre-radio-only entropy path after RF starts. */
mbedtls_ctr_drbg_set_reseed_interval(&s_drbg, INT_MAX);
s_generate_calls = 0U;
s_drbg_ready = true;
} else {
mbedtls_ctr_drbg_free(&s_drbg);
error = ESP_FAIL;
}
}
xSemaphoreGive(s_random_mutex);
return error;
}
esp_err_t secure_random_fill(void *output, size_t length)
{
if (length == 0U) {
return ESP_OK;
}
if (output == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (!s_drbg_ready || s_random_mutex == NULL) {
return ESP_ERR_INVALID_STATE;
}
unsigned char *cursor = (unsigned char *)output;
esp_err_t error = ESP_OK;
xSemaphoreTake(s_random_mutex, portMAX_DELAY);
while (length > 0U) {
/* CTR_DRBG limits each request even though the public API need not. */
size_t chunk = length;
if (chunk > MBEDTLS_CTR_DRBG_MAX_REQUEST) {
chunk = MBEDTLS_CTR_DRBG_MAX_REQUEST;
}
/*
* Mbed TLS stores its reseed counter in a signed int. Fail closed one
* call before INT_MAX so it can neither overflow nor invoke the
* pre-radio-only entropy callback during the device's lifetime.
*/
if (s_generate_calls >= (uint32_t)INT_MAX - 1U) {
error = ESP_ERR_INVALID_STATE;
break;
}
if (mbedtls_ctr_drbg_random(&s_drbg, cursor, chunk) != 0) {
error = ESP_FAIL;
break;
}
++s_generate_calls;
cursor += chunk;
length -= chunk;
}
xSemaphoreGive(s_random_mutex);
return error;
}
int secure_random_mbedtls(void *context, unsigned char *output, size_t length)
{
(void)context;
return secure_random_fill(output, length) == ESP_OK
? 0
: MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED;
}
void secure_wipe(void *data, size_t size)
{
volatile uint8_t *byte = (volatile uint8_t *)data;
if (byte == NULL) {
return;
}
while (size-- > 0U) {
*byte++ = 0U;
}
}