Update alert PSK handling to accept both base64 and hex formats

Enhanced the CLI command for setting the alert PSK to support both base64 (24/44 chars) and hex (32/64 chars) formats. This change allows for greater flexibility in key input, accommodating the mobile app's "share" output. Updated error messages for clarity and ensured that previously derived hashtags are cleared when a new PSK is set. Relevant adjustments made in CommonCLI and MQTT implementation documentation.
This commit is contained in:
agessaman committed 2026-05-12 21:05:56 -07:00
1 parent 6a3ed5d430
commit a865d0a303
2 files changed
+50 -19

No files matched your search

+3 -3
View File
@@ -714,7 +714,7 @@ A "recovered" message is sent once when the underlying connection comes back. Af
| Setting | Default | Notes |
|---------|---------|-------|
| `alert` | `off` | Master enable for automatic fault alerts |
| `alert.psk` | *(unset)* | Private base64 PSK (24 or 44 chars). The active channel key. |
| `alert.psk` | *(unset)* | Private channel secret. Accepts either base64 (24 or 44 chars, like `BaseChatMesh::addChannel`) or hex (32 or 64 chars, like the mobile app's "Share Channel" output). Stored internally as base64. |
| `alert.hashtag` | *(unset)* | Informational only; set via `set alert.hashtag` to pre-derive `alert.psk` from `sha256("#name")[0..15]`. Cleared when `alert.psk` is set directly. |
| `alert.region` | *(unset)* | Optional region name; overrides the repeater's `default_scope` for alert sends only. Empty = use `default_scope`. Looked up lazily via `RegionMap`; unknown names silently fall back to `default_scope`. |
| `alert.wifi` | `30` (min) | 0 disables WiFi alerts |
@@ -734,7 +734,7 @@ Get:
Set:
- `set alert on` / `set alert off`
- `set alert.psk <base64>` — 24-char (16-byte) or 44-char (32-byte) base64; rejects banned channels (Public, `#test`, `#bot`). Clears `alert.hashtag` since the new key is operator-supplied.
- `set alert.psk <secret>` — 24-/44-char base64 **or** 32-/64-char hex (16- or 32-byte secret); rejects banned channels (Public, `#test`, `#bot`). Clears `alert.hashtag` since the new key is operator-supplied. The mobile app's "share" output is hex; either format is accepted.
- `set alert.psk` (no argument) — clears both `alert.psk` and `alert.hashtag`
- `set alert.hashtag <name>` — derives the 16-byte key from `sha256("#name")` *once*, stores it as `alert.psk`, and remembers the hashtag for `get alert.hashtag`. `#` prefix is added if omitted (so `alerts` and `#alerts` are equivalent). Refuses banned hashtag names.
- `set alert.hashtag` (no argument) — clears both `alert.psk` and `alert.hashtag`
@@ -765,7 +765,7 @@ Anyone running a companion app and subscribed to the `#ops-alerts` hashtag chann
Generate a 16-byte random PSK and base64-encode it (24 chars), or use the companion app's "Add channel" feature to create one and copy the secret. Then:
```bash
set alert.psk <your_24_or_44_char_base64_psk>
set alert.psk <secret> # base64 (24/44 chars) or hex (32/64 chars) — mobile "share" works as-is
set alert.wifi 10
set alert.mqtt 60
set alert on
+47 -16
View File
@@ -1631,23 +1631,54 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
strcpy(reply, "Error: PSK too long (max 47 chars)");
} else if (val[0] == '#') {
strcpy(reply, "Error: use 'set alert.hashtag' for hashtag channels");
} else if (len != 24 && len != 44) {
// Quick character-count check before storing; AlertReporter will redo the
// full base64 decode and reject anything that doesn't yield 16 or 32 bytes.
strcpy(reply, "Error: PSK must be 24 chars (16-byte) or 44 chars (32-byte) base64");
} else if (const char* banned = alertReporterBannedChannelMatchB64(val)) {
// Refuse any key on the banned channel list (Public PSK, well-known
// auto-responder hashtags like #test/#bot, etc.). Fault alerts on those
// channels would spam every node in the area.
sprintf(reply, "Error: refusing banned channel '%s'; pick a private key or hashtag", banned);
} else {
StrHelper::strncpy(_prefs->alert_psk_b64, val, sizeof(_prefs->alert_psk_b64));
// The new PSK is operator-supplied, so any previously-derived hashtag
// name is no longer accurate provenance — drop it.
_prefs->alert_hashtag[0] = '\0';
savePrefs();
_callbacks->onAlertConfigChanged();
strcpy(reply, "OK - alert.psk updated");
// Accept either:
// - base64: 24 chars (16-byte secret) or 44 chars (32-byte secret) —
// the format `BaseChatMesh::addChannel` already expects.
// - hex: 32 chars (16-byte secret) or 64 chars (32-byte secret) —
// what the MeshCore mobile app's "share" button emits for both
// private channels and hashtag channels.
// Hex input is converted to base64 once at CLI time so the on-disk
// representation (and AlertReporter's decode path) stays unchanged.
char canonical_b64[48];
const char* store = nullptr;
if (len == 32 || len == 64) {
bool all_hex = true;
for (size_t i = 0; i < len; i++) {
if (!mesh::Utils::isHexChar(val[i])) { all_hex = false; break; }
}
if (all_hex) {
uint8_t raw[32];
int raw_len = (int)(len / 2);
if (mesh::Utils::fromHex(raw, raw_len, val)) {
size_t b64_len = alert_encode_base64(raw, (size_t)raw_len, canonical_b64, sizeof(canonical_b64));
if (b64_len > 0 && b64_len < sizeof(_prefs->alert_psk_b64)) {
store = canonical_b64;
}
}
}
}
if (!store && (len == 24 || len == 44)) {
store = val;
}
if (!store) {
strcpy(reply, "Error: PSK must be 32/64 hex chars or 24/44 chars base64 (16- or 32-byte secret)");
} else if (const char* banned = alertReporterBannedChannelMatchB64(store)) {
// Refuse any key on the banned channel list (Public PSK, well-known
// auto-responder hashtags like #test/#bot, etc.). Fault alerts on
// those channels would spam every node in the area.
sprintf(reply, "Error: refusing banned channel '%s'; pick a private key or hashtag", banned);
} else {
StrHelper::strncpy(_prefs->alert_psk_b64, store, sizeof(_prefs->alert_psk_b64));
// The new PSK is operator-supplied, so any previously-derived hashtag
// name is no longer accurate provenance — drop it.
_prefs->alert_hashtag[0] = '\0';
savePrefs();
_callbacks->onAlertConfigChanged();
strcpy(reply, "OK - alert.psk updated");
}
}
} else if (memcmp(config, "alert.hashtag", 13) == 0 && (config[13] == 0 || config[13] == ' ')) {
const char* val = (config[13] == ' ') ? &config[14] : "";