Add HTTPS identity rotation support
This commit is contained in:
@@ -11,13 +11,14 @@
|
||||
static uint8_t stored[1600], pending[1600];
|
||||
static size_t stored_size, pending_size;
|
||||
static int fault, writes, commits, rng_calls, legacy_wipes, groups;
|
||||
static bool locked, fail_mutex, fail_rng, fail_mac, alternate_mac, watch_publication;
|
||||
static bool locked, fail_mutex, fail_rng, fail_mac, alternate_mac, watch_publication, mutex_busy;
|
||||
static void (*crypto_hook)(void);
|
||||
static web_security_blob_t expected_live;
|
||||
|
||||
enum { OPEN_RO = 20, OPEN_RW, QUERY, READ, SET, COMMIT, TYPE, SHORT_READ };
|
||||
SemaphoreHandle_t xSemaphoreCreateMutex(void) { return fail_mutex ? NULL : (void *)1; }
|
||||
int xSemaphoreTake(SemaphoreHandle_t m, unsigned delay)
|
||||
{ (void)delay; assert(m && !locked); locked = true; return 1; }
|
||||
{ assert(m && !locked); if (mutex_busy) { assert(delay == 0); return 0; } locked = true; return 1; }
|
||||
int xSemaphoreGive(SemaphoreHandle_t m)
|
||||
{ assert(m && locked); locked = false; return 1; }
|
||||
esp_err_t esp_read_mac(uint8_t *mac, int type)
|
||||
@@ -32,6 +33,7 @@ esp_err_t secure_random_init(void) { return fail_rng ? ESP_FAIL : ESP_OK; }
|
||||
esp_err_t secure_random_fill(void *out, size_t length)
|
||||
{
|
||||
++rng_calls;
|
||||
if (crypto_hook) { void (*hook)(void) = crypto_hook; crypto_hook = NULL; hook(); }
|
||||
if (fail_rng) return ESP_FAIL;
|
||||
return getrandom(out, length, 0) == (ssize_t)length ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
@@ -68,7 +70,7 @@ esp_err_t nvs_get_blob(nvs_handle_t handle, const char *key, void *data, size_t
|
||||
esp_err_t nvs_set_blob(nvs_handle_t handle, const char *key, const void *data, size_t size)
|
||||
{
|
||||
assert(handle == NVS_READWRITE && !strcmp(key, "material"));
|
||||
assert(size == 1340 && locked);
|
||||
assert(size == 1340 && (s_identity_token ? !locked : locked));
|
||||
++writes;
|
||||
if (watch_publication) assert(!memcmp(&s_material, &expected_live, sizeof(s_material)));
|
||||
if (fault == SET) return ESP_FAIL;
|
||||
@@ -90,6 +92,8 @@ static void boot(void)
|
||||
{
|
||||
memset(&s_material, 0, sizeof(s_material));
|
||||
s_material_ready = false; s_security_mutex = NULL;
|
||||
s_identity_token = s_next_identity_token = 0; s_identity_used = false;
|
||||
crypto_hook = NULL; mutex_busy = false;
|
||||
s_load_result = WEB_SECURITY_LOAD_STORED;
|
||||
fault = writes = commits = rng_calls = legacy_wipes = 0;
|
||||
fail_mutex = fail_rng = fail_mac = alternate_mac = locked = false;
|
||||
@@ -124,6 +128,19 @@ static void rejected(void)
|
||||
assert(writes == 0 || fault == SET || fault == COMMIT);
|
||||
assert(web_security_rotate_certificate() == ESP_ERR_INVALID_STATE);
|
||||
}
|
||||
static void competing_identity(void)
|
||||
{
|
||||
assert(!locked && s_identity_token);
|
||||
web_security_identity_snapshot_t snapshot;
|
||||
assert(web_security_get_identity_snapshot(&snapshot) == ESP_OK && snapshot.busy);
|
||||
assert(snapshot.generation == expected_live.generation);
|
||||
assert(!memcmp(snapshot.fingerprint, expected_live.certificate_fingerprint, 32));
|
||||
assert(web_security_rotate_certificate() == ESP_ERR_INVALID_STATE);
|
||||
assert(web_security_reset_all() == ESP_ERR_INVALID_STATE);
|
||||
assert(web_security_init(NULL) == ESP_ERR_INVALID_STATE);
|
||||
assert(!memcmp(&expected_live, &s_material, sizeof(s_material)));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
boot(); stored_size = 0;
|
||||
@@ -262,6 +279,34 @@ int main(void)
|
||||
}
|
||||
group("rotation/reset transactional failures, identity change and generation increment");
|
||||
|
||||
uint32_t token = 0, generation = s_material.generation;
|
||||
int prior_writes = writes, prior_rng = rng_calls;
|
||||
assert(web_security_reserve_identity(generation - 1, false, &token) == ESP_ERR_INVALID_STATE && !token);
|
||||
assert(writes == prior_writes && rng_calls == prior_rng);
|
||||
web_security_identity_snapshot_t projection;
|
||||
mutex_busy = true;
|
||||
assert(web_security_get_identity_snapshot(&projection) == ESP_ERR_TIMEOUT);
|
||||
assert(web_security_reserve_identity(generation, false, &token) == ESP_ERR_TIMEOUT);
|
||||
mutex_busy = false;
|
||||
assert(web_security_reserve_identity(generation, false, &token) == ESP_OK && token);
|
||||
crypto_hook = competing_identity;
|
||||
assert(web_security_replace_reserved(token) == ESP_OK && !crypto_hook);
|
||||
assert(s_identity_token == token && s_material.generation == generation + 1);
|
||||
assert(web_security_replace_reserved(token) == ESP_ERR_INVALID_STATE);
|
||||
assert(web_security_reset_all() == ESP_ERR_INVALID_STATE);
|
||||
web_security_release_identity(token - 1); assert(s_identity_token == token);
|
||||
web_security_release_identity(token); assert(!s_identity_token);
|
||||
assert(web_security_replace_reserved(token) == ESP_ERR_INVALID_STATE);
|
||||
assert(web_security_get_identity_snapshot(&projection) == ESP_OK && !projection.busy);
|
||||
assert(!memcmp(projection.fingerprint, s_material.certificate_fingerprint, 32));
|
||||
expected_live = s_material;
|
||||
group("zero-wait public projection and stale/token fencing; real crypto outside locks excludes canonical writers through release");
|
||||
s_next_identity_token = UINT32_MAX;
|
||||
assert(web_security_reserve_identity(0, true, &token) == ESP_ERR_INVALID_STATE);
|
||||
assert(web_security_get_identity_snapshot(&projection) == ESP_OK && projection.busy);
|
||||
assert(!memcmp(&s_material, &expected_live, sizeof(s_material)));
|
||||
group("reservation IDs saturate without ABA or recovery mutation bypass");
|
||||
|
||||
for (int kind = 0; kind < 3; ++kind) {
|
||||
boot(); legacy(&identity);
|
||||
if (kind == 0) stored_size = 0;
|
||||
|
||||
Reference in New Issue
Block a user