Add SSH host identity rotation controls
This commit is contained in:
+83
-45
@@ -48,6 +48,9 @@ static bool s_mutex_creating;
|
||||
static ssh_security_blob_t s_material;
|
||||
static bool s_material_ready;
|
||||
static ssh_security_load_result_t s_load_result;
|
||||
static uint32_t s_identity_token, s_next_identity_token;
|
||||
static TaskHandle_t s_identity_owner;
|
||||
static bool s_identity_used;
|
||||
|
||||
static bool bytes_are_zero(const uint8_t *data, size_t size)
|
||||
{
|
||||
@@ -338,6 +341,10 @@ esp_err_t ssh_security_init(ssh_security_load_result_t *load_result)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (s_identity_token) {
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
ssh_security_blob_t candidate;
|
||||
bool missing = false;
|
||||
error = load_blob(&candidate, &missing);
|
||||
@@ -411,60 +418,91 @@ esp_err_t ssh_security_get_metadata(ssh_security_metadata_t *metadata)
|
||||
return error;
|
||||
}
|
||||
|
||||
esp_err_t ssh_security_rotate(void)
|
||||
esp_err_t ssh_security_get_identity_snapshot(ssh_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->metadata.generation = s_material.generation;
|
||||
memcpy(snapshot->metadata.sha256_fingerprint, s_material.sha256_fingerprint,
|
||||
sizeof(snapshot->metadata.sha256_fingerprint));
|
||||
snapshot->busy = s_identity_token != 0 || s_next_identity_token == UINT32_MAX;
|
||||
}
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
return error;
|
||||
}
|
||||
|
||||
esp_err_t ssh_security_reserve_identity(uint32_t generation, bool reset, uint32_t *token)
|
||||
{
|
||||
if (!token || (reset && generation)) return ESP_ERR_INVALID_ARG;
|
||||
*token = 0;
|
||||
if (reset) {
|
||||
esp_err_t error = secure_random_init();
|
||||
if (error == ESP_OK) error = ensure_mutex();
|
||||
if (error != ESP_OK) return error;
|
||||
}
|
||||
if (!s_security_mutex) return ESP_ERR_INVALID_STATE;
|
||||
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) ||
|
||||
(generation && generation != s_material.generation)) {
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
xSemaphoreTake(s_security_mutex, portMAX_DELAY);
|
||||
esp_err_t error = ESP_ERR_INVALID_STATE;
|
||||
ssh_security_blob_t candidate;
|
||||
memset(&candidate, 0, sizeof(candidate));
|
||||
if (s_material_ready && s_material.generation != UINT32_MAX) {
|
||||
error = generate_blob(&candidate, s_material.generation + 1U);
|
||||
if (error == ESP_OK) {
|
||||
error = save_blob(&candidate);
|
||||
}
|
||||
if (error == ESP_OK) {
|
||||
install_blob(&candidate);
|
||||
}
|
||||
}
|
||||
secure_wipe(&candidate, sizeof(candidate));
|
||||
*token = s_identity_token = ++s_next_identity_token;
|
||||
s_identity_owner = xTaskGetCurrentTaskHandle();
|
||||
s_identity_used = false;
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
return error;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ssh_security_reset(void)
|
||||
esp_err_t ssh_security_replace_reserved(uint32_t token)
|
||||
{
|
||||
esp_err_t error = secure_random_init();
|
||||
if (error != ESP_OK) {
|
||||
return error;
|
||||
}
|
||||
error = ensure_mutex();
|
||||
if (error != ESP_OK) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!s_security_mutex || !token) return ESP_ERR_INVALID_STATE;
|
||||
xSemaphoreTake(s_security_mutex, portMAX_DELAY);
|
||||
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 ||
|
||||
s_identity_owner != xTaskGetCurrentTaskHandle()) {
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
ssh_security_blob_t candidate;
|
||||
error = generate_blob(&candidate, generation);
|
||||
if (error == ESP_OK) {
|
||||
error = save_blob(&candidate);
|
||||
}
|
||||
if (error == ESP_OK) {
|
||||
install_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);
|
||||
|
||||
/* Reservation excludes writers while crypto and flash run outside locks. */
|
||||
ssh_security_blob_t candidate = {0};
|
||||
esp_err_t error = generate_blob(&candidate, generation);
|
||||
if (error == ESP_OK) error = save_blob(&candidate);
|
||||
xSemaphoreTake(s_security_mutex, portMAX_DELAY);
|
||||
if (error == ESP_OK) install_blob(&candidate);
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
secure_wipe(&candidate, sizeof(candidate));
|
||||
return error;
|
||||
}
|
||||
|
||||
void ssh_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_owner == xTaskGetCurrentTaskHandle()) {
|
||||
s_identity_token = 0;
|
||||
s_identity_owner = NULL;
|
||||
}
|
||||
xSemaphoreGive(s_security_mutex);
|
||||
}
|
||||
|
||||
static esp_err_t replace_identity(bool reset)
|
||||
{
|
||||
uint32_t token = 0;
|
||||
esp_err_t error = ssh_security_reserve_identity(0, reset, &token);
|
||||
if (error == ESP_OK) error = ssh_security_replace_reserved(token);
|
||||
ssh_security_release_identity(token);
|
||||
return error;
|
||||
}
|
||||
|
||||
esp_err_t ssh_security_rotate(void) { return replace_identity(false); }
|
||||
esp_err_t ssh_security_reset(void) { return replace_identity(true); }
|
||||
|
||||
Reference in New Issue
Block a user