From e70349a9fcf4741cb03ff8678165a9fc304982e5 Mon Sep 17 00:00:00 2001 From: Kemal Hadimli Date: Fri, 12 Jun 2026 09:44:40 +0100 Subject: [PATCH] filter channel: accept a #hashtag name, derive its key Hashtag channels key off the first 16 bytes of SHA256("#name") (same as MeshCore clients), so `filter channel #selftest` derives the key directly instead of needing a pasted PSK. Co-Authored-By: Claude Opus 4.8 --- docs/cli_commands.md | 2 ++ examples/simple_repeater/MyMesh.cpp | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 8f616aab9..243d081f5 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -1139,11 +1139,13 @@ Repeater only. Lets a repeater decrypt channels it holds the key for, inspect th #### Add or remove a channel to decrypt **Usage:** +- `filter channel <#name>` - `filter channel ` - `filter channel public` - `filter channel clear` **Parameters:** +- `#name`: A hashtag channel, given by its name (e.g. `#selftest`). The key is derived the same way MeshCore clients derive it — the first 16 bytes of `SHA256("#name")` — so you don't need to paste a key. Only works for hashtag channels (named channels like `public` use a random key, not a name-derived one). - `psk`: Channel pre-shared key, as either Base64 (the form MeshCore clients share, e.g. `izOH6cXN6mrJ5e26oRXNcg==`) or raw hex (`62be0e1267c401381f4ea6f44217d7a9`). Either way it must decode to 16 or 32 bytes. The literal `public` is a shortcut for the well-known public channel key. **Note:** Adding a channel only enables decryption/inspection of that channel; messages still forward normally unless they match a blocked keyword or sender. `filter channel clear` removes all channel keys (the repeater stops decrypting and forwards everything blind again). diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 972ecb005..2fc2df1b1 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -687,6 +687,11 @@ bool MyMesh::addFilterChannel(const char *psk) { if (strcmp(psk, "public") == 0) { memcpy(ch->secret, PUBLIC_CHANNEL_SECRET, sizeof(PUBLIC_CHANNEL_SECRET)); len = sizeof(PUBLIC_CHANNEL_SECRET); + } else if (psk[0] == '#') { // hashtag channel: key = first 16 bytes of SHA256(name) + uint8_t full[32]; + mesh::Utils::sha256(full, sizeof(full), (const uint8_t *)psk, strlen(psk)); + memcpy(ch->secret, full, 16); + len = 16; } else if (isHexKey(psk)) { len = strlen(psk) / 2; if (!mesh::Utils::fromHex(ch->secret, len, psk)) return false; @@ -913,7 +918,7 @@ void MyMesh::handleFilterCommand(char *command, char *reply) { strcpy(reply, "OK - filter reset"); return; } - strcpy(reply, "Err - usage: filter [list|stats [reset]|channel |block |sender |clear|reset]"); + strcpy(reply, "Err - usage: filter [list|stats [reset]|channel <#name|b64|hex|public|clear>|block |sender |clear|reset]"); } #endif // WITH_CHANNEL_FILTER