Add HTTPS identity rotation support
This commit is contained in:
+80
-56
@@ -51,6 +51,8 @@ _Static_assert(sizeof(web_security_blob_t) == WEB_SECURITY_BLOB_SIZE,
|
||||
static SemaphoreHandle_t s_security_mutex;
|
||||
static web_security_blob_t s_material;
|
||||
static bool s_material_ready;
|
||||
static uint32_t s_identity_token, s_next_identity_token;
|
||||
static bool s_identity_used;
|
||||
static web_security_load_result_t s_load_result;
|
||||
|
||||
static bool bytes_are_zero(const uint8_t *data, size_t size)
|
||||
@@ -649,6 +651,10 @@ esp_err_t web_security_init(web_security_load_result_t *load_result)
|
||||
}
|
||||
|
||||
xSemaphoreTake(s_security_mutex, portMAX_DELAY);
|
||||
if (s_identity_token) {
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (s_material_ready) {
|
||||
if (load_result != NULL) {
|
||||
*load_result = s_load_result;
|
||||
@@ -763,14 +769,6 @@ esp_err_t web_security_get_certificate_metadata(
|
||||
return error;
|
||||
}
|
||||
|
||||
static esp_err_t increment_generation(web_security_blob_t *blob)
|
||||
{
|
||||
if (blob->generation == UINT32_MAX) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
++blob->generation;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void install_committed_blob(const web_security_blob_t *candidate)
|
||||
{
|
||||
@@ -781,64 +779,90 @@ static void install_committed_blob(const web_security_blob_t *candidate)
|
||||
s_load_result = WEB_SECURITY_LOAD_STORED;
|
||||
}
|
||||
|
||||
esp_err_t web_security_rotate_certificate(void)
|
||||
esp_err_t web_security_get_identity_snapshot(web_security_identity_snapshot_t *snapshot)
|
||||
{
|
||||
if (s_security_mutex == NULL) {
|
||||
if (!snapshot) return ESP_ERR_INVALID_ARG;
|
||||
memset(snapshot, 0, sizeof(*snapshot));
|
||||
if (!s_security_mutex) return ESP_ERR_INVALID_STATE;
|
||||
if (xSemaphoreTake(s_security_mutex, 0U) != pdTRUE) return ESP_ERR_TIMEOUT;
|
||||
esp_err_t error = s_material_ready ? ESP_OK : ESP_ERR_INVALID_STATE;
|
||||
if (error == ESP_OK) {
|
||||
snapshot->generation = s_material.generation;
|
||||
memcpy(snapshot->fingerprint, s_material.certificate_fingerprint, sizeof(snapshot->fingerprint));
|
||||
snapshot->busy = s_identity_token != 0 || s_next_identity_token == UINT32_MAX;
|
||||
}
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
return error;
|
||||
}
|
||||
|
||||
esp_err_t web_security_reserve_identity(uint32_t expected_generation, bool reset, uint32_t *token)
|
||||
{
|
||||
if (!token || (reset && expected_generation)) return ESP_ERR_INVALID_ARG;
|
||||
*token = 0;
|
||||
if (!reset) {
|
||||
if (!s_security_mutex) return ESP_ERR_INVALID_STATE;
|
||||
if (xSemaphoreTake(s_security_mutex, 0U) != pdTRUE) return ESP_ERR_TIMEOUT;
|
||||
bool ready = s_material_ready;
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
if (!ready) return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
esp_err_t error = secure_random_init();
|
||||
if (error == ESP_OK) error = ensure_security_mutex();
|
||||
if (error != ESP_OK) return error;
|
||||
if (xSemaphoreTake(s_security_mutex, 0U) != pdTRUE) return ESP_ERR_TIMEOUT;
|
||||
if (s_identity_token || s_next_identity_token == UINT32_MAX ||
|
||||
(!s_material_ready && !reset) ||
|
||||
(s_material_ready && s_material.generation == UINT32_MAX) ||
|
||||
(expected_generation && expected_generation != s_material.generation)) {
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
*token = s_identity_token = ++s_next_identity_token;
|
||||
|
||||
xSemaphoreTake(s_security_mutex, portMAX_DELAY);
|
||||
esp_err_t error = ESP_ERR_INVALID_STATE;
|
||||
web_security_blob_t candidate;
|
||||
memset(&candidate, 0, sizeof(candidate));
|
||||
if (s_material_ready) {
|
||||
candidate = s_material;
|
||||
error = increment_generation(&candidate);
|
||||
if (error == ESP_OK) {
|
||||
error = generate_certificate(&candidate);
|
||||
}
|
||||
if (error == ESP_OK) {
|
||||
error = save_blob(&candidate);
|
||||
}
|
||||
if (error == ESP_OK) {
|
||||
install_committed_blob(&candidate);
|
||||
}
|
||||
}
|
||||
secure_wipe(&candidate, sizeof(candidate));
|
||||
s_identity_used = false;
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
return error;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t web_security_reset_all(void)
|
||||
esp_err_t web_security_replace_reserved(uint32_t token)
|
||||
{
|
||||
esp_err_t error = secure_random_init();
|
||||
if (error != ESP_OK) {
|
||||
return error;
|
||||
}
|
||||
error = ensure_security_mutex();
|
||||
if (error != ESP_OK) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!s_security_mutex || !token) return ESP_ERR_INVALID_STATE;
|
||||
xSemaphoreTake(s_security_mutex, portMAX_DELAY);
|
||||
web_security_blob_t candidate;
|
||||
uint32_t generation = 1U;
|
||||
if (s_material_ready) {
|
||||
if (s_material.generation == UINT32_MAX) {
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
generation = s_material.generation + 1U;
|
||||
if (s_identity_token != token || s_identity_used) {
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
error = generate_all(&candidate, generation);
|
||||
if (error == ESP_OK) {
|
||||
error = save_blob(&candidate);
|
||||
}
|
||||
if (error == ESP_OK) {
|
||||
install_committed_blob(&candidate);
|
||||
}
|
||||
secure_wipe(&candidate, sizeof(candidate));
|
||||
s_identity_used = true;
|
||||
uint32_t generation = s_material_ready ? s_material.generation + 1U : 1U;
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
|
||||
/* The reservation, not a held mutex/spinlock, excludes all identity writers. */
|
||||
web_security_blob_t candidate = {0};
|
||||
esp_err_t error = generate_all(&candidate, generation);
|
||||
if (error == ESP_OK) error = save_blob(&candidate);
|
||||
xSemaphoreTake(s_security_mutex, portMAX_DELAY);
|
||||
if (error == ESP_OK) install_committed_blob(&candidate);
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
secure_wipe(&candidate, sizeof(candidate));
|
||||
return error;
|
||||
}
|
||||
|
||||
void web_security_release_identity(uint32_t token)
|
||||
{
|
||||
if (!s_security_mutex || !token) return;
|
||||
xSemaphoreTake(s_security_mutex, portMAX_DELAY);
|
||||
if (s_identity_token == token) s_identity_token = 0;
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
}
|
||||
|
||||
static esp_err_t replace_identity(bool reset)
|
||||
{
|
||||
uint32_t token = 0;
|
||||
esp_err_t error = web_security_reserve_identity(0, reset, &token);
|
||||
if (error == ESP_OK) error = web_security_replace_reserved(token);
|
||||
web_security_release_identity(token);
|
||||
return error;
|
||||
}
|
||||
|
||||
esp_err_t web_security_rotate_certificate(void) { return replace_identity(false); }
|
||||
esp_err_t web_security_reset_all(void) { return replace_identity(true); }
|
||||
|
||||
Reference in New Issue
Block a user