From a427735788a25bf6c2027971f04426bbc12c9190 Mon Sep 17 00:00:00 2001 From: Kemal Hadimli Date: Thu, 11 Jun 2026 23:39:03 +0100 Subject: [PATCH] Store channel filter config in binary, drop the PSK string array Keys are never displayed (filter list shows counts only), so persist the decoded GroupChannel directly instead of the original PSK string. Removes the 272-byte filter_channel_psk[] buffer and the base64/hex round-trip; config file is now a versioned binary blob. Co-Authored-By: Claude Opus 4.8 --- examples/simple_repeater/MyMesh.cpp | 69 +++++++++++++++++++++-------- examples/simple_repeater/MyMesh.h | 3 +- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 33cb1b43e..972ecb005 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -696,7 +696,6 @@ bool MyMesh::addFilterChannel(const char *psk) { if (len != 16 && len != 32) return false; mesh::Utils::sha256(ch->hash, sizeof(ch->hash), ch->secret, len); - StrHelper::strncpy(filter_channel_psk[num_filter_channels], psk, FILTER_PSK_LEN); num_filter_channels++; return true; } @@ -781,32 +780,64 @@ void MyMesh::loadChannelFilter() { #endif if (!f) return; - char line[FILTER_PSK_LEN + 8]; - while (f.available()) { - int n = f.readBytesUntil('\n', (uint8_t *)line, sizeof(line) - 1); - line[n] = 0; - while (n > 0 && (line[n - 1] == '\r' || line[n - 1] == ' ')) line[--n] = 0; - if (n < 3 || line[1] != ' ') continue; - - const char *val = &line[2]; - if (line[0] == 'C') { - addFilterChannel(val); - } else if (line[0] == 'K' && num_block_keywords < MAX_FILTER_TERMS) { - StrHelper::strncpy(block_keywords[num_block_keywords++], val, FILTER_TERM_LEN); - } else if (line[0] == 'S' && num_block_senders < MAX_FILTER_TERMS) { - StrHelper::strncpy(block_senders[num_block_senders++], val, FILTER_TERM_LEN); - } + if (f.read() != CHANNEL_FILTER_FMT) { f.close(); return; } // unknown/legacy file -> start empty + + int n = f.read(); + if (n < 0 || n > MAX_FILTER_CHANNELS) { f.close(); return; } + for (int i = 0; i < n; i++) { + auto ch = &filter_channels[num_filter_channels]; + if (f.readBytes((char *)ch->hash, sizeof(ch->hash)) != sizeof(ch->hash)) break; + if (f.readBytes((char *)ch->secret, sizeof(ch->secret)) != sizeof(ch->secret)) break; + num_filter_channels++; + } + + n = f.read(); + if (n < 0 || n > MAX_FILTER_TERMS) { f.close(); return; } + for (int i = 0; i < n; i++) { + int l = f.read(); + if (l < 0 || l >= FILTER_TERM_LEN) break; + if (f.readBytes(block_keywords[num_block_keywords], l) != (size_t)l) break; + block_keywords[num_block_keywords][l] = 0; + num_block_keywords++; + } + + n = f.read(); + if (n < 0 || n > MAX_FILTER_TERMS) { f.close(); return; } + for (int i = 0; i < n; i++) { + int l = f.read(); + if (l < 0 || l >= FILTER_TERM_LEN) break; + if (f.readBytes(block_senders[num_block_senders], l) != (size_t)l) break; + block_senders[num_block_senders][l] = 0; + num_block_senders++; } f.close(); } +// Binary layout: [fmt][n_chan]{hash[1] secret[32]}* [n_kw]{len text}* [n_snd]{len text}* +// Channel keys are never displayed, so the decoded GroupChannel is stored directly +// rather than the original PSK string. void MyMesh::saveChannelFilter() { _fs->remove(CHANNEL_FILTER_FILE); File f = openAppend(CHANNEL_FILTER_FILE); if (!f) return; - for (int i = 0; i < num_filter_channels; i++) f.printf("C %s\n", filter_channel_psk[i]); - for (int i = 0; i < num_block_keywords; i++) f.printf("K %s\n", block_keywords[i]); - for (int i = 0; i < num_block_senders; i++) f.printf("S %s\n", block_senders[i]); + f.write((uint8_t)CHANNEL_FILTER_FMT); + f.write((uint8_t)num_filter_channels); + for (int i = 0; i < num_filter_channels; i++) { + f.write(filter_channels[i].hash, sizeof(filter_channels[i].hash)); + f.write(filter_channels[i].secret, sizeof(filter_channels[i].secret)); + } + f.write((uint8_t)num_block_keywords); + for (int i = 0; i < num_block_keywords; i++) { + uint8_t l = strlen(block_keywords[i]); + f.write(l); + f.write((const uint8_t *)block_keywords[i], l); + } + f.write((uint8_t)num_block_senders); + for (int i = 0; i < num_block_senders; i++) { + uint8_t l = strlen(block_senders[i]); + f.write(l); + f.write((const uint8_t *)block_senders[i], l); + } f.close(); } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 97c8cefff..a62592259 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -90,7 +90,7 @@ struct NeighbourInfo { #define MAX_FILTER_TERMS 8 #endif #define FILTER_TERM_LEN 24 -#define FILTER_PSK_LEN 68 // fits a 64-char hex key, a 44-char base64 PSK, or "public" +#define CHANNEL_FILTER_FMT 1 // on-disk format version for CHANNEL_FILTER_FILE #endif class MyMesh : public mesh::Mesh, public CommonCLICallbacks { @@ -129,7 +129,6 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { #ifdef WITH_CHANNEL_FILTER mesh::GroupChannel filter_channels[MAX_FILTER_CHANNELS]; - char filter_channel_psk[MAX_FILTER_CHANNELS][FILTER_PSK_LEN]; uint8_t num_filter_channels; char block_keywords[MAX_FILTER_TERMS][FILTER_TERM_LEN]; uint8_t num_block_keywords;