From c93bc84b0abf74fb8972d57d2720ae5614e37760 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 24 Sep 2026 19:17:28 -0700 Subject: [PATCH] Cover ESP32 migration identity recovery in host tests --- test/fixtures/contact_cache/mocks/Identity.h | 6 ++ test/test_identity_and_settings_recovery.py | 60 ++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/test/fixtures/contact_cache/mocks/Identity.h b/test/fixtures/contact_cache/mocks/Identity.h index d33a1840..a1696f7d 100644 --- a/test/fixtures/contact_cache/mocks/Identity.h +++ b/test/fixtures/contact_cache/mocks/Identity.h @@ -16,6 +16,12 @@ public: return in.read(pub_key, sizeof(pub_key)) == sizeof(pub_key) && in.read(private_key, sizeof(private_key)) == sizeof(private_key); } + void readFrom(const uint8_t* src, size_t len) { + if (len == sizeof(private_key) + sizeof(pub_key)) { + memcpy(private_key, src, sizeof(private_key)); + memcpy(pub_key, src + sizeof(private_key), sizeof(pub_key)); + } + } size_t writeTo(uint8_t* out, size_t size) const { if (size < 96) return 0; memcpy(out, private_key, 64); diff --git a/test/test_identity_and_settings_recovery.py b/test/test_identity_and_settings_recovery.py index 272f3966..9385b4dc 100644 --- a/test/test_identity_and_settings_recovery.py +++ b/test/test_identity_and_settings_recovery.py @@ -51,6 +51,41 @@ class IdentityAndSettingsRecovery(unittest.TestCase): source = (ROOT / 'src/helpers/IdentityStore.cpp').read_text() presence = (ROOT / 'src/helpers/FilePresence.h').read_text() program = fixture_prefix() + '\n#include \n' + if platform == 'ESP32_PLATFORM': + program += r''' +struct MigrationNvs { + bool pending = false; + std::vector identity; +} migration_nvs; +class Preferences { +public: + bool begin(const char* name, bool) { + assert(strcmp(name, "mesh-pt-migrate") == 0); + return true; + } + bool getBool(const char* key, bool fallback) { + assert(strcmp(key, "id-pending") == 0); + return migration_nvs.pending ? true : fallback; + } + size_t getBytes(const char* key, void* out, size_t length) { + assert(strcmp(key, "identity") == 0); + const size_t count = std::min(length, migration_nvs.identity.size()); + if (count) memcpy(out, migration_nvs.identity.data(), count); + return count; + } + bool remove(const char* key) { + if (strcmp(key, "id-pending") == 0) { + const bool existed = migration_nvs.pending; + migration_nvs.pending = false; + return existed; + } + assert(strcmp(key, "identity") == 0); + migration_nvs.identity.clear(); + return true; + } + void end() {} +}; +''' program += 'namespace mesh { template \n' + extract_braced( presence, 'bool filePresence(') + '\n}\n' for signature in ('bool IdentityStore::recover(', @@ -156,6 +191,31 @@ int main() { filesystem.fail_rename = 0; assert(recovering.recover("main") && filesystem.files["/main.id"].size() == 96); #endif +#if defined(ESP32_PLATFORM) + // A pending partition migration must replace a temporary key with the + // original one, and keep its NVS copy until the file is durable. + filesystem = FakeFilesystem(); + migration_nvs = MigrationNvs(); + IdentityStore migrated(filesystem, "/identity"); + mesh::LocalIdentity temporary, restored; + memset(temporary.pub_key, 9, PUB_KEY_SIZE); + memset(temporary.private_key, 10, PRV_KEY_SIZE); + assert(migrated.save("_main", temporary)); + migration_nvs.pending = true; + migration_nvs.identity.resize(PUB_KEY_SIZE + PRV_KEY_SIZE); + memset(migration_nvs.identity.data(), 7, PUB_KEY_SIZE); + memset(migration_nvs.identity.data() + PUB_KEY_SIZE, 8, PRV_KEY_SIZE); + filesystem.max_write = 3; + assert(migrated.loadResult("_main", restored) == IdentityLoadResult::Unreadable); + assert(migration_nvs.pending && migration_nvs.identity.size() == 96); + filesystem.max_write = std::numeric_limits::max(); + assert(migrated.loadResult("_main", restored) == IdentityLoadResult::Loaded); + assert(restored.pub_key[0] == 7 && restored.private_key[0] == 8); + assert(!migration_nvs.pending && migration_nvs.identity.empty()); + mesh::LocalIdentity again; + assert(migrated.load("_main", again)); + assert(again.pub_key[0] == 7 && again.private_key[0] == 8); +#endif } ''' compile_run(program, platform)