Commit Graph
106 Commits
Author SHA1 Message Date
liquidraver 4ec6f27202 password compare bug fix 2026-06-16 12:36:54 +02:00
liquidraver dc7a2827a3 sync with vanilla dev 2026-06-15 15:25:04 +02:00
liquidraver 572490e909 migrate to NVS, update formatters 2026-06-15 14:08:09 +02:00
liquidraver 5fc8fd1ead shared canonical path for UI adverts 2026-06-14 22:25:32 +02:00
liquidraver bf71542751 Make Rxdutycycling Great Again 2026-06-13 08:23:01 +02:00
liquidraver 966bf24c96 defer MQTT connect publish to main loop (uplink cross-thread fix) 2026-06-11 12:32:37 +02:00
liquidraver 2ad185bab4 license "refactor" :) 2026-06-07 22:47:17 +02:00
liquidraver 2cf4b9704e BT DFU fixes + power bump up 2026-06-07 22:33:50 +02:00
liquidraver 7c46f4a343 legacy code cleanup 2026-06-07 13:02:07 +02:00
liquidraver 01fd56a573 wire up RTC to out-of-the-box capable nodes 2026-06-07 12:00:05 +02:00
liquidraver ffe9ea652d sync to vanilla 1.16 2026-06-06 16:42:39 +02:00
liquidraver d13d6f4621 version to 1.15.9 2026-06-05 14:59:12 +02:00
liquidraver e052f3d231 sync with upstream dev 2026-06-03 22:01:23 +02:00
liquidraver 743076b850 bump some magic numbers 2026-06-03 14:02:46 +02:00
liquidraver 8761a82d93 fix USB protocol handling and bump version 2026-06-03 10:09:43 +02:00
liquidraver 876e818b00 cleanup repeat function in room servers more 2026-06-03 07:33:20 +02:00
liquidraver ad4c28cdb0 unbreak build #1 2026-06-03 07:25:24 +02:00
rlwilliamson-dev f3da3f7cee room-server: drop repeater forwarding, neighbour tracking and discovery
A room server is an endpoint, not a repeater. Strip the repeater
machinery the role inherited from the RepeaterMesh clone:

- allowPacketForward() returns false unconditionally — never relays
  transit traffic. (Upstream's simple_room_server gates this on
  disable_fwd; hard-off here since the role should never repeat.)
- neighbour tracking: NeighbourInfo/neighbours[], putNeighbour,
  onAdvertRecv, removeNeighbor; formatNeighborsReply -> "not supported"
- node discovery: onControlDataRecv, sendNodeDiscoverReq, the
  discover.neighbors CLI, discover rate-limiter + pending state
- loop detection: isLooped + max_loop_* tables + loop_detect pref
- dead code: RepeaterStats struct, GET_NEIGHBOURS handler, simple_sort

-344 lines net. Builds clean (FLASH 30.7% / RAM 32.2%); login, post,
push/sync, read-only guest and admin remote-management all
hardware-verified on the GAT562 kit.
2026-06-02 18:19:22 -05:00
rlwilliamson-dev 05fce66b16 room-server: drop dead anon-request handlers inherited from the clone
The room server's onAnonDataRecv handles login inline (the room login
protocol carries a sync_since cursor the repeater's doesn't), so the
cloned repeater helpers handleLoginReq + handleAnonRegionsReq /
handleAnonOwnerReq / handleAnonClockReq — and the anon_limiter they used
— were dead after the port. Remove them (~160 lines). No behavior change.

The inherited neighbor-tracking / node-discovery machinery is also unused
on a room server; left in place for now (can be trimmed in a follow-up).
2026-06-02 15:43:29 -05:00
rlwilliamson-dev fcd5fd8641 room-server: fix login when a password is set over a longer one
StrHelper::strncpy null-terminates but does NOT zero-pad the 16-byte
password buffer, so setting a shorter password over a longer previous
value (e.g. one inherited from a prior repeater config) leaves trailing
garbage. onAnonDataRecv's constant-time compare runs over the full
buffer width, so a correct password stopped matching — admin/guest
logins were silently rejected, or downgraded to a read-only guest when
allow_read_only was on (the login looked identical to read-only).

Fix: copy both stored passwords into zeroed buffers (up to strnlen)
before the constant-time compare, so the comparison reflects the actual
string while staying constant-time over the full width.

Hardware-verified on the kit: admin login with the correct password now
grants ADMIN (post + remote management), confirmed server-side via
get acl (perms 03).

Note: the repeater's handleLoginReq shares this latent issue.
2026-06-02 15:07:50 -05:00
rlwilliamson-dev 58dbba7f3b room-server: drive push engine on a timer + cut delivery latency
Two hardware-found fixes after on-air testing on the GAT562 kit:

- Add a 500ms push timer in main_room_server.cpp so the post-sync
  engine advances at its intended cadence. ZephCore is event-driven
  (no Arduino superloop), so without this the engine only ran on the
  5s housekeeping tick — posts dripped out every ~5s and transmits
  bunched up, causing timeouts/resends.

- Lower the post-sync hold from upstream's conservative defaults
  (POST_SYNC_DELAY 6s -> 2s, PUSH_NOTIFY 2000ms -> 1000ms). These are
  server-side timing only (no wire-format change), and take measured
  delivery from ~6-7s down to ~2s.

Verified on hardware (910.525/62.5/SF7/CR8, two clients): normal-pace
messages deliver in ~2s with clean ACKs. Rapid-fire bursts can still
drop out-of-order messages via the per-client timestamp replay check
(unchanged from upstream) — left as-is to stay upstream-compatible.
2026-06-02 14:12:08 -05:00
rlwilliamson-dev bb6d9c60e1 app: add Room Server (BBS) role
Add the MeshCore Room Server role to ZephCore — a store-and-forward
shared message room. Clients log in with an admin or guest password
and post messages; the server pushes each new post to all other
logged-in clients (round-robin, per-client sync cursor, ACK + retry,
3-strike eviction).

Ported from upstream MeshCore's simple_room_server, structured as a
near-clone of RepeaterMesh so it reuses the proven ACL, region
filtering, CLI, adverts and telemetry; the post buffer + push engine
are the only net-new pieces:
- RoomServerMesh: PostInfo ring (MAX_UNSYNCED_POSTS=32), addPost,
  pushPostToClient, getUnsyncedCount, processAck, onAckRecv, the
  loop() push driver; room login (onAnonDataRecv parses the
  sync_since cursor); posts/admin-CLI/keep-alive (onPeerDataRecv);
  ADV_TYPE_ROOM advert; ServerStats wire layout; disable_fwd=1.
- main_room_server.cpp: event-loop entry (USB serial CLI, no BLE).
- Kconfig: ZEPHCORE_ROLE_ROOM_SERVER + ZEPHCORE_MAX_UNSYNCED_POSTS.
- CMakeLists role gating; boards/common/room_server.conf.

Post frame, SHA-256 ACK and login-reply layouts match upstream for
MeshCore app compatibility. Builds for gat562_30s
(FLASH 30.95% / RAM 32.46%). Not yet hardware-tested.
2026-06-02 13:02:57 -05:00
liquidraver 99e05e3b03 sync with vanilla dev 2026-06-02 14:50:18 +02:00
liquidraver 09074d8852 refactor(crypto): replace orlp/ed25519 with Monocypher 4.0.2
Swap the vendored orlp/ed25519 (frozen ~2017 ref10) for Monocypher
4.0.2, an actively maintained, audited, single-file implementation.

The persisted private key keeps its 64-byte expanded layout
(clamped SHA-512(seed) scalar a || nonce prefix), so identities
written by older firmware load, sign, verify and key-exchange
unchanged -- no re-key, no storage migration, full wire
compatibility with the existing mesh and Arduino MeshCore.

Because the stored key carries no seed for Monocypher's high-level
EdDSA API, Identity drives signing from the low-level primitives
(crypto_sha512 + crypto_eddsa_reduce/scalarbase/mul_add); verify
uses crypto_ed25519_check and ECDH uses crypto_eddsa_to_x25519 +
crypto_x25519. Nonce material is now wiped after signing.

Validated byte-for-byte against the previous orlp output via a
known-answer harness (keygen, sign-from-expanded-key, verify
accept/reject, X25519-over-Ed25519 shared secret) before the swap.

Frees ~42 KB of flash: orlp linked ~55 KB (dominated by its ~30 KB
ref10 precomputed tables); the Monocypher Ed25519/X25519/SHA-512
paths link ~14 KB, with --gc-sections dropping all unused algos.
wio_tracker_l1 pristine build: FLASH 54.14%, links clean.

Monocypher is CC0-1.0 OR BSD-2-Clause.
2026-05-31 22:07:43 +02:00
liquidraver 65b5371a65 fix(repeater): flood anon reply when the return path is rejected
The three anon handlers ignored copyPath's return value: on a rejected
(over-long) path, reply_path_len kept the attacker byte while reply_path
stayed stale, so the reply went out with a corrupt direct path. Reset to
OUT_PATH_UNKNOWN so it floods instead; keep the legit zero-length case.
2026-05-31 15:36:31 +02:00
liquidraver ea27b93ac5 refactor: split RepeaterMesh into RepeaterUplink + RepeaterRegionCLI 2026-05-29 15:25:18 +02:00
liquidraver 94dcf61715 simplify app layer: dedup response/telemetry/JSON builders
- CompanionMesh: sendPacketSent() helper, collapse sendFloodScoped
  overloads, share self-telemetry LPP builder
- Mesh: shared computeAdaptive{Flood,Direct}Delay (was duplicated in
  Companion + Repeater)
- ObserverMesh/RepeaterMesh: shared helpers/MeshcoreJson.h builders;
  drop dead sign_input_len
2026-05-29 12:54:52 +02:00
liquidraver b692ca72ed crypto: harden all crypto-sensitive memcmp + memset sites
Audit-driven sweep found additional compiler-optimization-sensitive
patterns beyond the login password compare just fixed:

P4.F3 (HIGH) — Utils::MACThenDecrypt verified packet MACs with
plain memcmp. Runs on EVERY encrypted-then-MAC'd packet in the
mesh; a timing oracle here lets attackers forge MACs byte-by-byte
across the whole mesh layer. Replaced with constantTimeEqual.

P4.F4 (MEDIUM) — Multiple memset(secret, 0, ...) calls on
stack-resident crypto buffers (Ed25519 seed, ADC noise pool, AES
key derived in extract_via_aes_ctr, HWINFO unique ID) were
subject to dead-store elimination under -Os. GCC/Clang routinely
elide these when the buffer is never read after; the wipe vanishes
and the secret persists on stack until next call overwrites.
Replaced with secureZeroize using volatile pointer writes.

P4.F5 (LOW) — Identity::validatePrivateKey boot self-test compared
shared secrets with plain memcmp. Boot-only, no attacker
observation channel, but hygiene matters and the fix is one line.
Also added secret-wipe for ss1/ss2 on all return paths.

Promoted the local ct_memeq() previously added to RepeaterMesh.cpp
into Utils::constantTimeEqual + Utils::secureZeroize (Utils.h/cpp)
so the login compare and MAC compare share the same audited helper.

Both helpers verified by Thumb-2 disassembly on rak3401_1watt:
- constantTimeEqual: loop branches on iterator, accumulator
  load-modify-stored to stack every iteration, final return uses
  CLZ+LSR (no conditional branch on result).
- secureZeroize: STRB.W to memory in a counted loop, not replaced
  with memset builtin and not eliminated.
2026-05-28 13:26:20 +02:00
liquidraver 88dccf2e24 crypto: switch login password compare to volatile-based ct_memeq
mbedtls_ct_memcmp is declared in the tf-psa-crypto header but its
implementation isn't compiled into the current Zephyr mbedtls build
(would require enabling additional TLS features). Use a local
ct_memeq() with `volatile uint8_t` accumulator instead — pattern
matches rweather/arduinolibs Crypto.cpp secure_compare().

Disassembly verified on rak3401_1watt (Thumb-2): loop branches on
the iterator pointer not the accumulator, result is load-modify-
stored to stack every iteration (volatile preserved), final return
uses clz+shift instead of a conditional branch on the value.

Spotted by nextgens during review of meshcore-dev/MeshCore#2556
mitigations.
2026-05-28 13:08:18 +02:00
liquidraver 8d138c41b3 crypto: mitigate login plaintext-password vulnerability (server-side)
Tracks upstream meshcore-dev/MeshCore#2556 — passwords sent as
plaintext over encrypted links are vulnerable to evil-twin/phishing
attacks (attacker advertises a repeater with the same name but a
different pubkey; user picks the wrong one and types the password).

The structural fix is a protocol change (PAKE adoption — SPAKE2,
OPAQUE, or HMAC-with-both-pubkeys) and must land synchronously
across all implementations. Diverging unilaterally would break
interop with Arduino-based companions and repeaters, so we wait
for upstream.

Two within-protocol mitigations applied server-side:

1. Constant-time password comparison.  Replaced strcmp() in
   RepeaterMesh::handleLoginReq with a local ct_memeq() helper.
   Pads the received password to the full 16-byte storage size,
   XOR-accumulates byte differences with no early exit. Compares
   both admin and guest passwords unconditionally so timing is
   identical regardless of which (if any) the attempt resembled.
   Eliminates the timing oracle that lets an already-MITM
   attacker recover the stored password byte-by-byte.

2. Failed-login rate limit.  New login_fail_limiter(4, 180)
   RateLimiter — 4 wrong-password attempts per 180s, matching the
   existing anon_limiter pattern. Hitting the cap trips a distinct
   LOG_WRN so operators see active brute-force attempts in logs.
   Global rate (not per-sender) — simpler, no ACL state bloat;
   trade-off documented in CRYPTO_AUDIT_INDEX.md.

What's NOT fixed: the wire protocol still carries plaintext
passwords. The evil-twin attack itself remains possible; these
mitigations raise the attacker's cost (no timing leak, no
brute-force at line rate) but don't replace the structural fix.
UI-side defenses (TOFU warnings on duplicate names, pubkey
fingerprint display) are valuable companion-side mitigations
but out of scope for this audit's server-side commit.
2026-05-28 09:49:28 +02:00
liquidraver 99279fd9fa sync with vanilla dev 2026-05-24 21:07:38 +02:00
liquidraver 051adef93e native linux initial commit 2026-05-24 20:06:49 +02:00
liquidraver c7a00b9533 update version 2026-05-22 15:39:07 +02:00
liquidraver 6b624a69e5 sync with vnailla dev 2026-05-22 14:42:12 +02:00
liquidraver 094ea6e8a3 joystick UI:
- track real RTC sync source on the joystick Time screen
- gate joystick-only UI helpers behind stub headers so non-joystick builds skip the extra code without #ifdef at every call site.
2026-05-22 11:38:51 +02:00
liquidraver 322460fa78 joystick UI:
make UI show actual time source
2026-05-22 11:29:25 +02:00
liquidraver 4f388c0b98 companion: fix "62 hops" garbage in local-sent BLE mirror frames
The phone app interprets path_len with an unconditional `& 63`, so
the OUT_PATH_SENT (0xFE) sentinel we were writing into the
offline-queue frame became "62 hops" + 4 bogus path bytes in the
app's UI. There are 0 LoRa hops between the sender (us) and the
phone viewing the message — path_len = 0 is both correct and
renders cleanly as "direct / 0 hops". The "(>>✓) " / "(>>✗) " body
prefix still distinguishes wio-originated messages from incoming.

OUT_PATH_SENT remains the marker for the joystick UI's own local
entries (UnreadScreen, _ch_previews) — formatHopCount handles it
explicitly so it's safe there.
2026-05-22 09:11:12 +02:00
liquidraver 03fcc60fa9 joystick UI: channel-send heard-repeat feedback
After broadcasting a group message, wait 5s to see if any neighbor
repeated the flood and use the outcome to mark the on-device entry
and the BLE-app mirror.

  - ContentionTracker gets extractDupeCount(hash): finds the tracked
    entry, captures dupe_count, finalizes (folds into EMA, marks
    inactive), returns the count or -1.
  - BaseChatMesh::sendGroupMessage gains an optional out_hash param;
    when set, the FNV-1a packet hash is also pre-registered with the
    contention tracker so heard retransmits get counted (originated
    floods weren't tracked before, only relays).
  - Mesh::getContentionTracker() promoted to public so the UI can
    query after the feedback window.

JoystickUITask grows a 4-slot pending-channel table with per-slot
k_timer (5s one-shot). startPendingChannel() broadcasts, adds the
local _ch_previews entry with path_len = OUT_PATH_SENT, and starts
the feedback timer. The timer ISR sets a feedback_due flag; the
loop's processPendingChannelFeedback() picks it up, calls
extractDupeCount(), and rewrites the preview's path_len to
OUT_PATH_SENT_HEARD (0xFD) or OUT_PATH_SENT_UNHEARD (0xFC) — which
formatHopCount renders as "sent+" / "sent?".

The deferred BLE-app mirror queues only on outcome with body prefix
"(>>✓) " (heard) or "(>>✗) " (not heard). queueLocalSentChannelMessage
gains a heard_repeat parameter for the selection.

Both channel send entry points (sendComposedMessage's channel branch
and sendChannelMessage) now route through startPendingChannel().
2026-05-21 22:24:26 +02:00
liquidraver 8e5e54a61e joystick UI: DM retry, force-flood fallback, BLE mirror on outcome
Outgoing DMs now go through a 4-slot pending-send table with per-slot
one-shot k_timer. On no-ACK the message retries (up to 5 attempts,
0-indexed); attempt 4 clears recipient.out_path_len + markContactsDirty
so the last try forces flood and future DMs re-discover the path.

ACK dispatch: CompanionMesh::processAck tries _ack_table first
(phone-initiated sends), then ui_joystick_try_match_ack() for joystick-
initiated sends, then falls through to connection-keepalive ACKs.

BLE-app mirror is now deferred until outcome is known. The body prefix
in the offline-queue frame is "(>>✓) " on delivery or "(>>✗) " on
failure, replacing the previous unconditional "(>>) ". UnreadScreen's
sent-entry origin prefix is updated in place via markSentEntryStatus()
to "(>>+) " / "(>>X) " (ASCII for the OLED font).

queueLocalSentContactMessage gains a 'delivered' parameter to pick the
prefix; markContactsDirtyPublic() exposes the lazy-write trigger so
the joystick's path-clear persists.
2026-05-21 22:09:56 +02:00
liquidraver ec2ae31b67 joystick UI: prefix sender to mirrored channel sends for BLE app
BaseChatMesh::sendGroupMessage wraps the body as "<sender_name>: <body>"
before transmitting (the receiver's onChannelMessageRecv sees the full
"USER: MSG" string, which CompanionMesh queues as-is for the phone).
queueLocalSentChannelMessage was queuing just the raw body, so the
phone parsed an empty sender and lost the body. Prepend
"<prefs.node_name>: " to mirror the wire format.

joystick UI: prefix DM mirrors to phone with sent-marker

DM offline-queue frames identify the sender by pubkey field, not by
in-text prefix. queueLocalSentContactMessage was queuing with the
contact's pubkey + raw body, so the phone app rendered wio-originated
DMs identically to incoming ones from that contact. Prepend "(>>) "
to the body so sent messages are visually distinguishable; matches
the joystick UI's own sent indicator (UnreadScreen::addPreview).
2026-05-21 13:41:13 +02:00
liquidraver 3de179f9bc joystick UI: lock-screen info, BLE-aware unread, mirror local sends
- Lock overlay: show battery % + unread count between title and unlock
  sequence; ui_invalidate_battery_cache() on screen wake forces a fresh
  ADC sample so the user sees current data immediately
- UnreadScreen::addPreview gains initially_read; received msgs pass
  _ble_connected (don't count unread when phone is syncing); sent msgs
  pass true (you sent it, you know it)
- CompanionMesh::queueLocalSent{Contact,Channel}Message + PUSH_CODE_MSG_WAITING
  on wio-originated sends so a connected phone app sees them via the
  normal offline-queue flow (path_len = OUT_PATH_SENT marker)
- OUT_PATH_SENT moved back from joystick_defs.h to ContactInfo.h
  (now a wire-format value, not UI-only)
2026-05-21 09:37:24 +02:00
liquidraver e36d9b33a3 companion: scope joystick-only state to joystick builds
- #ifdef-gate _pending_joystick_{ping,admin}_tag fields + setters
  under CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK (saves 8 bytes per
  CompanionMesh instance on button-UI builds)
- gate logTx ui_notify_packet_sent() to joystick builds only;
  was firing on every TX for any UI variant (dead code on button UI)
- drop redundant _pending_login manual set in CMD_SEND_LOGIN;
  BaseChatMesh::sendLogin's onLoginSent hook owns it now, just
  clear the other pending fields explicitly
2026-05-21 09:06:55 +02:00
Steve Calvário 56b10f70a1 Init 2026-05-20 20:40:58 +01:00
liquidraver 0ba2721b40 refactor(companion): tighten Phase 2F polish — error codes, length checks, build assert
Three small correctness/polish improvements from BLE audit Phase 2F:

1. Five handlers (CMD_APP_START, CMD_GET_CHANNEL, CMD_SET_CHANNEL,
   CMD_DEVICE_QUERY, CMD_SEND_CHANNEL_TXT_MSG) previously responded
   with ERR_UNSUPPORTED on short-frame validation failure (because
   they fell through to the dispatcher's default break, which the
   caller converts to "unknown command"). They now explicitly
   sendPacketError(ERR_ILLEGAL_ARG) — the semantically correct code
   for "known cmd, bad frame".

2. CMD_SET_TUNING_PARAMS previously returned PACKET_OK on short
   frames without applying any change. Now sends ERR_ILLEGAL_ARG so
   the phone learns the change didn't take.

3. Added static_assert that CONFIG_ZEPHCORE_BOARD_NAME fits in 40
   bytes including its null terminator, so a future too-long board
   name fails at build time instead of producing an unterminated
   wire-format response.
2026-05-20 15:54:20 +02:00
liquidraver b39483add3 fix(companion): null-terminate contact name in CMD_ADD_UPDATE_CONTACT
The wire format reserves a 32-byte name field; if the phone sends 32
non-null bytes, ContactInfo::name has no terminator. Subsequent
LOG_INF/LOG_DBG sites using %s with contact.name then read past the
field into adjacent struct bytes (type, flags, out_path_len, ...)
until the first null. No memory corruption — serializeContact uses
StrHelper::strzcpy which is length-bounded — but log output gets
garbage and a paired peer could probe a few bytes of the struct
through log capture.

Sibling handler CMD_SET_CHANNEL at :1593-1594 already does this
defensively. Match the pattern.
2026-05-20 15:40:18 +02:00
liquidraver 988b438ec3 refactor(companion): harden telemetry buffer sizing and custom-vars snprintf
Two polish items from BLE audit Phase 2B:

1. CMD_SEND_TELEMETRY_REQ self-response buffer was uint8_t rsp[96]
   with a comment claiming 70 B worst case. Actual worst case at
   POWER_MAX_CHANNELS=4 is 82 B; if the channel cap ever grew the
   buffer would silently overflow. Replaced with a sizeof-style
   expression that tracks POWER_MAX_CHANNELS, plus an 8-byte safety
   pad. No size change today (90 vs. 96) but the upper bound auto-
   tracks any future bump.

2. CMD_GET_CUSTOM_VARS used `dp += snprintf(dp, 20, ...)` which
   advances by the would-be-written length, not bytes actually
   written. Currently safe only because gps_interval is capped
   ≤86400, but if either cap drifted or a new key was added the
   length passed to writeFrame would include uninitialized stack
   bytes between the truncation point and the (over-advanced) dp.
   Now tracks rsp_end, computes remaining per snprintf, and only
   advances dp on real progress.

Both are correctness polish, not exploitable today.
2026-05-20 11:57:37 +02:00
liquidraver bd1e022e88 fix(security): close OOB read in path-decoding callers (BLE + LoRa-anon)
Both mesh::Packet::writePath and ::copyPath did a raw memcpy of the
decoded hash_count*hash_size bytes from src to dest with no bound on
src. Two call sites used phone-supplied or LoRa-anon-supplied buffers
where the path_len byte was attacker-controlled:

  - CompanionMesh CMD_SEND_CHANNEL_DATA accepted len>=4 and called
    writePath with no src bound; a paired phone could leak up to ~65
    bytes of syswq stack into the outgoing LoRa channel-data frame.

  - RepeaterMesh handleAnonRegionsReq / handleAnonOwnerReq /
    handleAnonClockReq read reply_path_len from an unauthenticated
    LoRa anon-request payload and called copyPath without any src
    bound. Any LoRa neighbor could leak repeater stack into the
    reply path.

Hardened the API: both functions now require an explicit src_len
and reject (return 0) when the decoded byte count exceeds it.
Updated all 14 call sites across Packet/Mesh/Dispatcher/BaseChatMesh/
CompanionMesh/RepeaterMesh. Trusted callers (internal MAX_PATH_SIZE
buffers) pass MAX_PATH_SIZE; untrusted callers pass real remaining
length. Added len-5 plumbing through the anon-handler signatures.

CMD_SEND_CHANNEL_DATA also gained a local len>=5 + path_bytes
sanity check for early rejection.
2026-05-20 11:52:39 +02:00
liquidraver d7e420bf2f fix(ble,usb): three bugs from BLE audit
1. USB takeover opcode mismatch
   ZephyrCompanionUSB.cpp checked payload[0] == 0x03 with a comment
   claiming CMD_APP_START, but CMD_APP_START is 0x01 (0x03 is
   CMD_SEND_CHANNEL_TXT_MSG). The USB handshake silently dropped the
   companion app's first frame on every connection; the app appeared
   broken over USB until the user happened to send a channel message.

2. CMD_SET_ADVERT_NAME didn't propagate to BLE adv data
   Name changes were persisted to prefs but the advertising payload
   and GATT device name kept the old value until reboot. Added
   zephcore_ble_update_name() and called it from the handler.

3. No advertising-health watchdog
   If bt_le_adv_start() ever failed transiently (HCI timeout,
   controller pacing), the device would silently stop advertising
   and stay undiscoverable until reboot. Added an adv_running flag
   and a 5s watchdog in the companion housekeeping handler that
   nudges adv back on if it stops outside a connection. Tracks
   Arduino nrf52's equivalent 10s watchdog.
2026-05-20 11:09:45 +02:00
liquidraver a6d095bc16 edit default prefs 2026-05-12 22:07:02 +02:00
liquidraver 2ee0fffa69 sync with vanilla 2026-05-08 10:13:42 +02:00