Ikoka Handheld compile and LED fixes#35
Closed
andyshinn wants to merge 93 commits intoweebl2000:dev_plusfrom
Closed
Ikoka Handheld compile and LED fixes#35andyshinn wants to merge 93 commits intoweebl2000:dev_plusfrom
andyshinn wants to merge 93 commits intoweebl2000:dev_plusfrom
Conversation
andyshinn
commented
Mar 2, 2026
- Fix compiling on Ikoka Handheld
- Fix LED inverted
- Add status LED
The GC1109 FEM needs its VFEM_Ctrl pin held HIGH during deep sleep to keep the LNA active, enabling proper RX sensitivity for wake-on-packet. Without this, the LNA is unpowered during sleep and RX wake sensitivity is degraded by ~17dB. Release RTC holds in begin() after configuring GPIO registers (not before) to ensure glitch-free pin transitions on wake. Trade-off: ~6.5mA additional sleep current for significantly improved wake-on-packet range.
- runtime auto-detection of v4.3 board (KCT8103L FEM) vs V4.0-V4.2 (GC1109) via GPIO2 pull level - different TX/RX path using PGIO5 for KCT8103L, GPIO46 CPS for GC1109 - hold both FEMs active for RX - report Heltec V4.3 in manufacturer name
Use millisHasNowPassed() (2's complement safe) instead of direct comparison, consistent with the repeater's sleep timing logic. Co-Authored-By: Wessel <weebl@users.noreply.github.com>
When BLE is enabled but no phone has been connected for 60 seconds, enter a 12s sleep / 3s awake cycle to conserve power while remaining discoverable. On wake, BLE advertising is restarted so phones can reconnect. The cycle exits immediately when a connection is detected. Adds hasPendingConnection() to BaseSerialInterface (defaults to isConnected()). The ESP32 BLE override uses getConnectedCount() > 0 to detect mid-bonding connections, preventing sleep during the authentication handshake. Guarded by #ifndef WIFI_SSID — WiFi builds are unaffected.
The onContactResponse handler copies peer response data into out_frame (MAX_FRAME_SIZE + 1 bytes) without checking whether the data fits. A peer response with len close to MAX_PACKET_PAYLOAD (184) writes up to 188 bytes into the 173-byte buffer, overflowing by 15 bytes. This affects the status response, telemetry response, and binary response code paths. A malicious peer can trigger the overflow by sending a large response payload, corrupting the stack. Cap each memcpy to the remaining space in out_frame before copying.
The TRACE forwarding path appends an SNR byte to pkt->path via path_len++, but the guard only checked path_len < MAX_PATH_SIZE. When path_len entered as MAX_PATH_SIZE - 1, the write was in-bounds but left path_len equal to MAX_PATH_SIZE, which could cause off-by-one issues in downstream code that uses path_len as an index. Change the guard to path_len + 1 < MAX_PATH_SIZE so there is always room for the append without path_len reaching MAX_PATH_SIZE.
Add ChaCha20-Poly1305 AEAD decryption with 4-byte auth tag for peer messages and group channels, falling back to ECB for backward compatibility. Sending remains ECB-only in this phase. - Per-message key derivation: HMAC-SHA256(secret, nonce||dest||src) - Direction-dependent keys prevent bidirectional keystream reuse - 12-byte IV from nonce + dest_hash + src_hash - Advertise AEAD capability via feat1 bit 0 in adverts - Track peer AEAD support in ContactInfo.flags - Seed aead_nonce from HW RNG on contact creation and load
Send ChaChaPoly-encrypted messages to peers with CONTACT_FLAG_AEAD set, and try AEAD decode first for those peers (avoiding 1/65536 ECB false-positive). Legacy peers continue to use ECB in both directions. - Add aead_nonce parameter to createDatagram/createPathReturn (default 0 = ECB) - Add getPeerFlags/getPeerNextAeadNonce virtual methods for decode-order selection - Add ContactInfo::nextAeadNonce() helper (returns nonce++ if AEAD, 0 otherwise) - Update all BaseChatMesh send paths to pass nonce for AEAD-capable peers - Adaptive decode order: AEAD-first for known AEAD peers, ECB-first for others
The header's route type bits (PH_ROUTE_MASK) are zero when createDatagram/createPathReturn encrypt with AEAD, but get changed to ROUTE_TYPE_FLOOD (1) or ROUTE_TYPE_DIRECT (2) by sendFlood/sendDirect afterwards. The receiver builds assoc from the received header (with route bits set), so the tag check always fails and every AEAD packet is silently dropped. Mask out route type bits in assoc data on all 5 encrypt/decrypt sites. Also track AEAD decode success to enable peer capability auto-detection.
- Fix potential unsigned overflow in createDatagram size check by subtracting constants from MAX_PACKET_PAYLOAD instead of adding to data_len - Add upper-bound validation on src_len and assoc_len in aeadEncrypt and aeadDecrypt - Log peer name on AEAD nonce wraparound for debug builds
Prevent nonce reuse after reboots by persisting per-peer nonce counters to a dedicated /nonces (companion) or /s_nonces (server) file. On dirty reset (power-on, watchdog, brownout), nonces are bumped by NONCE_BOOT_BUMP (100) to cover any unpersisted messages. Clean wakes (deep sleep, software restart) load nonces as-is. - Add nonce persistence to BaseChatMesh (companion) and ClientACL (server) - Add wasDirtyReset() helper to ArduinoHelpers.h for platform-specific reset reason detection (ESP32/NRF52) - Add onBeforeReboot() callback to CommonCLI for pre-reboot nonce flush - Wire nonce persistence into all firmware variants: companion radio, repeater, room server, and sensor - Only clear dirty flag on successful file write
The fs->remove("/contacts3.bak") before the rename sequence creates a
vulnerability window: if power is lost right after removing the backup
but before the rename completes, both the backup and primary file could
be lost. The remove is unnecessary since rename() on both SPIFFS and
LittleFS replaces the target if it already exists.
readFrom reads the header byte, transport codes (4 bytes), and path_len from the source buffer before any length validation. With a short input, these reads go past the end of the buffer. Add upfront length checks: minimum 2 bytes overall, transport codes require 4 additional bytes, and path must fit before the remaining payload.
The HMAC check in MACThenDecrypt used standard memcmp(), which short-circuits on the first mismatched byte. This makes the comparison time dependent on how many bytes of the MAC are correct, leaking information through a timing side-channel. With a 2-byte MAC (65,536 possible values), an attacker on a local interface (serial, BLE, or WiFi) can measure response latency to distinguish "first byte wrong" from "first byte correct, second wrong". This reduces a brute-force from 65,536 attempts down to roughly 384 (256 + 128 on average), making MAC forgery practical. An attacker could use this to forge packets that pass MAC verification without knowing the shared secret, allowing them to inject arbitrary messages that appear to come from a trusted peer. Replace memcmp with a constant-time XOR-accumulate loop so the comparison always takes the same time regardless of which bytes match.
Replace the fixed-size hash table with an LRU cache using actual timestamps instead of timeout-based eviction. Some busy nodes see more than 128 packets before duplicates arrive, so LRU ordering provides better eviction behavior.
…yload bounds checks
…y-one fix and payload_len>=9 check
…-detection' into dev_plus
… bounds checks (3 sites)
… simpler deep_sleep_lna)
…n-bucket' into dev_plus
There was a problem hiding this comment.
Pull request overview
This PR updates the Ikoka Handheld NRF52 variant configuration to fix build settings and align LED behavior with the board’s wiring, including adding a dedicated status LED pin.
Changes:
- Fix Ikoka Handheld build configuration by setting the PlatformIO board + linker script and correcting env inheritance.
- Fix inverted LED logic by updating
LED_STATE_ONfor this variant. - Add a
PIN_STATUS_LEDdefinition and initialize it during board startup.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| variants/ikoka_handheld_nrf/variant.h | Updates LED active state and adds PIN_STATUS_LED mapping. |
| variants/ikoka_handheld_nrf/platformio.ini | Adds board/ldscript config and fixes env extends to use the correct base section. |
| variants/ikoka_handheld_nrf/IkokaNrf52Board.cpp | Initializes the status LED pin at startup. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.