From 8427ea278b928b29bd2fe722f930c5e0fc137c4c Mon Sep 17 00:00:00 2001 From: zach Date: Sun, 14 Dec 2025 17:47:30 -0700 Subject: [PATCH] Refactor the saveIdentity method to include the apply_now parameter in several mesh implementations. This change will allow for the conditional application of new identities. Update the related header files and add support for fast RNG for key regeneration in CommonCLI. --- examples/simple_repeater/MyMesh.cpp | 6 +- examples/simple_repeater/MyMesh.h | 9 +- examples/simple_room_server/MyMesh.cpp | 6 +- examples/simple_room_server/MyMesh.h | 3 +- examples/simple_sensor/SensorMesh.cpp | 6 +- examples/simple_sensor/SensorMesh.h | 3 +- src/helpers/CommonCLI.cpp | 129 +++++++++++++++++++++++++ src/helpers/CommonCLI.h | 4 +- 8 files changed, 153 insertions(+), 13 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 5fb1a729f..01821f9e9 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -882,8 +882,8 @@ void MyMesh::formatPacketStatsReply(char *reply) { getNumRecvFlood(), getNumRecvDirect()); } -void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) { - self_id = new_id; +void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id, bool apply_now) { + if (apply_now) self_id = new_id; #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) IdentityStore store(*_fs, ""); #elif defined(ESP32) @@ -893,7 +893,7 @@ void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) { #else #error "need to define saveIdentity()" #endif - store.save("_main", self_id); + store.save("_main", new_id); } void MyMesh::clearStats() { diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index ed9f0c5fc..9b0b2ff1f 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -201,7 +201,14 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { mesh::LocalIdentity& getSelfId() override { return self_id; } - void saveIdentity(const mesh::LocalIdentity& new_id) override; + void saveIdentity(const mesh::LocalIdentity& new_id, bool apply_now = true) override; + mesh::LocalIdentity generateNewIdentity() override { return radio_new_identity(); } + bool hasNeighborWithHash(uint8_t hash) override { + for (int i = 0; i < MAX_NEIGHBOURS; i++) { + if (neighbours[i].heard_timestamp > 0 && neighbours[i].id.pub_key[0] == hash) return true; + } + return false; + } void clearStats() override; void handleCommand(uint32_t sender_timestamp, char* command, char* reply); void loop(); diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 60dd18407..a00382fbd 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -719,8 +719,8 @@ void MyMesh::setTxPower(uint8_t power_dbm) { radio_set_tx_power(power_dbm); } -void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) { - self_id = new_id; +void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id, bool apply_now) { + if (apply_now) self_id = new_id; #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) IdentityStore store(*_fs, ""); #elif defined(ESP32) @@ -730,7 +730,7 @@ void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) { #else #error "need to define saveIdentity()" #endif - store.save("_main", self_id); + store.save("_main", new_id); } void MyMesh::clearStats() { diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index e7f1fee83..70bc414f0 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -201,7 +201,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { static bool saveFilter(ClientInfo* client); - void saveIdentity(const mesh::LocalIdentity& new_id) override; + void saveIdentity(const mesh::LocalIdentity& new_id, bool apply_now = true) override; + mesh::LocalIdentity generateNewIdentity() override { return radio_new_identity(); } void clearStats() override; void handleCommand(uint32_t sender_timestamp, char* command, char* reply); void loop(); diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 4995c55fc..8430dc63b 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -764,8 +764,8 @@ bool SensorMesh::formatFileSystem() { #endif } -void SensorMesh::saveIdentity(const mesh::LocalIdentity& new_id) { - self_id = new_id; +void SensorMesh::saveIdentity(const mesh::LocalIdentity& new_id, bool apply_now) { + if (apply_now) self_id = new_id; #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) IdentityStore store(*_fs, ""); #elif defined(ESP32) @@ -775,7 +775,7 @@ void SensorMesh::saveIdentity(const mesh::LocalIdentity& new_id) { #else #error "need to define saveIdentity()" #endif - store.save("_main", self_id); + store.save("_main", new_id); } void SensorMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) { diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index c320eb447..e82dd75df 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -74,7 +74,8 @@ class SensorMesh : public mesh::Mesh, public CommonCLICallbacks { void formatRadioStatsReply(char *reply) override; void formatPacketStatsReply(char *reply) override; mesh::LocalIdentity& getSelfId() override { return self_id; } - void saveIdentity(const mesh::LocalIdentity& new_id) override; + void saveIdentity(const mesh::LocalIdentity& new_id, bool apply_now = true) override; + mesh::LocalIdentity generateNewIdentity() override { return radio_new_identity(); } void clearStats() override { } void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index a3de990aa..b3983b6f8 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -4,6 +4,64 @@ #include "AdvertDataHelpers.h" #include +// Platform-specific fast RNG includes +#if defined(ESP32) + #include +#elif defined(NRF52_PLATFORM) + #include +#elif defined(STM32_PLATFORM) + extern RNG_HandleTypeDef hrng; +#elif defined(RP2040_PLATFORM) + #include +#endif + +// Fast hardware RNG +class FastRNG : public mesh::RNG { +public: + void random(uint8_t* dest, size_t sz) override { +#if defined(ESP32) + esp_fill_random(dest, sz); +#elif defined(NRF52_PLATFORM) + uint8_t avail = 0; + while (sz > 0) { + sd_rand_application_bytes_available_get(&avail); + if (avail > 0) { + uint8_t chunk = (avail < sz) ? avail : sz; + sd_rand_application_vector_get(dest, chunk); + dest += chunk; + sz -= chunk; + } + } +#elif defined(STM32_PLATFORM) + while (sz >= 4) { + uint32_t r; + HAL_RNG_GenerateRandomNumber(&hrng, &r); + memcpy(dest, &r, 4); + dest += 4; + sz -= 4; + } + if (sz > 0) { + uint32_t r; + HAL_RNG_GenerateRandomNumber(&hrng, &r); + memcpy(dest, &r, sz); + } +#elif defined(RP2040_PLATFORM) + while (sz > 0) { + uint32_t r = get_rand_32(); + uint8_t chunk = (sz < 4) ? sz : 4; + memcpy(dest, &r, chunk); + dest += chunk; + sz -= chunk; + } +#else + //Fallback + for (size_t i = 0; i < sz; i++) { + dest[i] = ::random(256); + } +#endif + } +}; + // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { uint32_t n = 0; @@ -556,6 +614,77 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "%s (Build: %s)", _callbacks->getFirmwareVer(), _callbacks->getBuildDate()); } else if (memcmp(command, "board", 5) == 0) { sprintf(reply, "%s", _board->getManufacturerName()); + } else if (memcmp(command, "regen key", 9) == 0 && (command[9] == 0 || command[9] == ' ')) { + // Parse optional target hash: "regen key" or "regen key XX" + uint8_t target_hash = 0; + bool has_target = false; + if (command[9] == ' ' && command[10] != 0) { + // Validate hex characters before parsing + if (!mesh::Utils::isHexChar(command[10]) || !mesh::Utils::isHexChar(command[11])) { + strcpy(reply, "Error: invalid hex (use 0-9, A-F)"); + return; + } + uint8_t parsed[1]; + if (mesh::Utils::fromHex(parsed, 1, &command[10])) { + target_hash = parsed[0]; + has_target = true; + if (target_hash == 0x00 || target_hash == 0xFF) { + strcpy(reply, "Error: 00 and FF are reserved hashes"); + return; + } + } else { + strcpy(reply, "Error: invalid hash (use 2 hex chars, e.g. A3)"); + return; + } + } + + Serial.println("Generating key, please wait..."); + mesh::LocalIdentity new_id; + int attempts = 0; + int max_attempts = has_target ? 1000 : 50; + bool found = false; + +#if !defined(RP2040_PLATFORM) + FastRNG fast_rng; +#endif + while (attempts < max_attempts) { +#if defined(RP2040_PLATFORM) + new_id = _callbacks->generateNewIdentity(); // use radio noise for RP2040 +#else + new_id = mesh::LocalIdentity(&fast_rng); +#endif + uint8_t hash = new_id.pub_key[0]; + attempts++; + + if (has_target) { + if (hash == target_hash) { + found = true; + break; + } + } else { + if (hash != 0x00 && hash != 0xFF && !_callbacks->hasNeighborWithHash(hash)) { + found = true; + break; + } + } + } + + if (!found) { + if (has_target) { + sprintf(reply, "Error: hash %02X not found after %d attempts", target_hash, attempts); + } else { + strcpy(reply, "Error: could not find non-colliding hash"); + } + } else { + _callbacks->saveIdentity(new_id, false); // save but don't apply until reboot + char hex[5]; + mesh::Utils::toHex(hex, new_id.pub_key, 2); + if (has_target && _callbacks->hasNeighborWithHash(new_id.pub_key[0])) { + sprintf(reply, "WARNING: collision! new ID: %s (reboot to apply)", hex); + } else { + sprintf(reply, "OK - new ID: %s (reboot to apply)", hex); + } + } } else if (memcmp(command, "sensor get ", 11) == 0) { const char* key = command + 11; const char* val = _sensors->getSettingByKey(key); diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 068783ab1..89e1044a1 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -72,7 +72,9 @@ class CommonCLICallbacks { virtual void formatRadioStatsReply(char *reply) = 0; virtual void formatPacketStatsReply(char *reply) = 0; virtual mesh::LocalIdentity& getSelfId() = 0; - virtual void saveIdentity(const mesh::LocalIdentity& new_id) = 0; + virtual void saveIdentity(const mesh::LocalIdentity& new_id, bool apply_now = true) = 0; + virtual mesh::LocalIdentity generateNewIdentity() = 0; + virtual bool hasNeighborWithHash(uint8_t hash) { return false; } virtual void clearStats() = 0; virtual void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) = 0;