Skip to content

Sign plaintext packets in licensed mode - #10969

Merged
vidplace7 merged 11 commits into
meshtastic:developfrom
RCGV1:codex/ham-mode-signing
Jul 27, 2026
Merged

Sign plaintext packets in licensed mode#10969
vidplace7 merged 11 commits into
meshtastic:developfrom
RCGV1:codex/ham-mode-signing

Conversation

@RCGV1

@RCGV1 RCGV1 commented Jul 10, 2026

Copy link
Copy Markdown
Member

Closes #10966

Design: meshtastic/design#122
Related receive-policy work: #10967
Related schema: meshtastic/protobufs#983

Summary

  • generate or restore the existing identity keypair for licensed operation and publish its public key in NodeInfo;
  • sign fitting licensed broadcasts and directed packets with the existing XEdDSA implementation and existing from | id | portnum | payload tuple;
  • keep licensed traffic plaintext by preserving the PKI send prohibition, blocking licensed PKI receive, and persistently removing channel PSKs/admin channels;
  • warn before key-derived NodeNum migration, including deferred notification for legacy licensed nodes migrated during boot;
  • sanitize licensed channel state during owner changes, cold boot/reload, and backup restore.

This does not add an authentication version, signature format, certificate system, or new wire-auth scheme. Normal-mode broadcast signing and PKI direct messages are unchanged. A signed callsign-bearing NodeInfo self-binds that label to a key; it does not validate licence ownership, provide confidentiality, or by itself prevent replay.

Validation

  • test_admin_radio: 72/72 passed
  • test_packet_signing: 39/39 passed
  • rak3172: successful build
    • RAM: 25,012 / 65,536 (38.2%)
    • flash: 183,464 / 247,808 (74.0%)
  • Trunk: no new issues
  • git diff --check: clean
  • Independent security review completed for commit 155c4f76289e760bd789d36f3254105f6b8e1702

Draft / hardware status

No RF hardware verification has been performed. Before this leaves draft:

  • capture licensed broadcast and directed packets over RF and confirm the Data payload remains readable plaintext;
  • verify signatures across representative devices and cross-version peers;
  • verify reboot, licensed-mode transition, and backup-restore sanitation on hardware;
  • verify Strict first-contact acceptance together with feat(security): enforce packet authenticity policies #10967 after its schema dependency is available.

Operators remain responsible for their licence conditions, jurisdiction, permitted modes, and station-identification requirements.

Summary by CodeRabbit

  • New Features
    • Added a client warning notification for pending licensed-identity migration requiring identity regeneration.
    • Extended LoRa region validation and region-picker compatibility for prospective licensed-owner flows.
  • Bug Fixes
    • Improved licensed-channel sanitization and persistence: updates only when needed and stays idempotent across owner/config changes, boot defense, and preference restores.
    • Tightened licensed PKI behavior and updated signing rules for licensed and broadcast traffic.
    • Licensed node information now retains/publishes the licensed public key.
  • Reliability
    • Licensed-operation cleanup/state saving now occurs only when required, reducing repeated writes.

RCGV1 added 3 commits July 9, 2026 18:04
Preserve and publish identity keys in licensed mode so existing XEdDSA signatures can authenticate plaintext traffic. Sign licensed broadcasts and direct messages when they fit, while keeping PKI encryption disabled and normal routing behavior unchanged.
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6c891682-ff3b-4360-9bd1-b8cfb158d242

📥 Commits

Reviewing files that changed from the base of the PR and between 93c4f67 and 2baf37b.

📒 Files selected for processing (2)
  • src/mesh/Router.cpp
  • test/test_packet_signing/test_main.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/mesh/Router.cpp
  • test/test_packet_signing/test_main.cpp

📝 Walkthrough

Walkthrough

Licensed operation now generates and publishes identity keys, signs eligible plaintext traffic, prevents PKI decryption, sanitizes channels, and persists migration changes across boot, configuration, and restore flows.

Changes

Licensed operation

Layer / File(s) Summary
Licensed packet signing and identity publication
src/mesh/Router.cpp, src/modules/NodeInfoModule.cpp, test/test_packet_signing/test_main.cpp, test/support/MockMeshService.h
Licensed packets are signed when their encoded data fits, licensed PKI candidates are rejected without decryption, and NodeInfo retains the public identity key.
Licensed identity migration lifecycle
src/mesh/NodeDB.*, src/modules/AdminModule.*, src/main.cpp, test/support/*, test/test_packet_signing/test_main.cpp
Identity migration detection, key generation, deferred notification, configuration handling, persistence tracking, and test harness support are added.
Licensed channel sanitization and persistence
src/mesh/Channels.cpp, src/mesh/NodeDB.cpp, src/modules/AdminModule.cpp, test/test_admin_radio/test_main.cpp
Licensed channel cleanup runs during owner changes, boot loading, and preference restoration, with corrected channel state persisted.
Region validation and coverage
src/mesh/RadioInterface.*, src/graphics/draw/MenuHandler.cpp, test/test_admin_radio/test_main.cpp
Prospective licensed region validation is supported and tested alongside licensed configuration and sanitation flows.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AdminModule
  participant NodeDB
  participant MeshService
  participant Router
  participant NodeInfoModule

  AdminModule->>NodeDB: generate licensed identity key
  NodeDB->>NodeDB: mark migration pending
  NodeDB->>MeshService: notify pending identity migration
  Router->>Router: sign eligible plaintext licensed packet
  Router->>Router: skip PKI decryption for licensed traffic
  NodeInfoModule->>NodeInfoModule: publish identity public key
Loading

Possibly related issues

  • meshtastic/design#122 — Defines the licensed identity-key publication, plaintext signing, and PKI exclusion implemented here.
  • meshtastic/design#123 — Covers overlapping licensed plaintext signing and identity handling.
  • meshtastic/firmware#10971 — Modifies the Router authentication path supporting licensed signed plaintext traffic.

Possibly related PRs

Suggested labels: needs-review

Suggested reviewers: caveman99, jp-bennett, thebentern

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description does not follow the repository template and omits the required attestations checklist. Reformat the description to the provided template and include the attestations section with checked testing and hardware details.
Docstring Coverage ⚠️ Warning Docstring coverage is 31.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title concisely matches the main change: signing plaintext licensed-mode packets.
Linked Issues check ✅ Passed The changes satisfy #10966 by preserving plaintext licensed traffic, publishing the public key, signing eligible packets, and handling migration and sanitation.
Out of Scope Changes check ✅ Passed The added test harness, API, and menu/region adjustments all support the licensed-signing work and do not appear unrelated.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Flash this PR in the Web Flasher

firmware commit boards expires

Warning

This is an automated, unreviewed CI test build. Back up your device configuration
before flashing, and only flash devices you are able to recover.

Supported boards built by this PR (31)
Device Board Platform
Crowpanel Adv 3.5 TFT elecrow-adv-35-tft esp32-s3
Heltec HT62 heltec-ht62-esp32c3-sx1262 esp32-c3
Heltec Mesh Node 096 heltec-mesh-node-t096 nrf52840
Heltec Mesh Node T1 heltec-mesh-node-t1 nrf52840
Heltec Mesh Node T114 heltec-mesh-node-t114 nrf52840
Heltec V3 heltec-v3 esp32-s3
Heltec V4 heltec-v4 esp32-s3
Meshnology W10 meshnology_w10 esp32-s3
Meshnology W12 meshnology_w12 esp32-s3
Raspberry Pi Pico pico rp2040
Raspberry Pi Pico W picow rp2040
RAK WisMesh Pocket V3 rak_wismesh_pocket nrf52840
RAK WisMesh Pod rak_wismesh_pod nrf52840
RAK WisMesh Repeater Mini V2 rak_wismesh_repeater_mini nrf52840
RAK WisMesh Tag rak_wismeshtag nrf52840
RAK WisBlock 11200 rak11200 esp32
RAK WisBlock 11310 rak11310 rp2040
RAK3312 rak3312 esp32-s3
RAK WisBlock 4631 rak4631 nrf52840
Seeed SenseCAP Mesh-Tracker-X1 seeed_mesh_tracker_X1 nrf52840
Seeed Wio Tracker L1 seeed_wio_tracker_L1 nrf52840
Seeed Xiao NRF52840 Kit seeed_xiao_nrf52840_kit nrf52840
Seeed Xiao ESP32-S3 seeed-xiao-s3 esp32-s3
Station G2 station-g2 esp32-s3
Station G3 station-g3 esp32-s3
LILYGO T-Deck t-deck-tft esp32-s3
LILYGO T-Echo t-echo nrf52840
LILYGO T-Echo Plus t-echo-plus nrf52840
LILYGO T-Impulse Plus t-impulse-plus nrf52840
LilyGo T3-C6 tlora-c6 esp32-c6
Seeed SenseCAP T1000-E tracker-t1000-e nrf52840

Build artifacts expire on 2026-08-25. Updated for 79f8a43.

@RCGV1

RCGV1 commented Jul 10, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@RCGV1

RCGV1 commented Jul 10, 2026

Copy link
Copy Markdown
Member Author

CI label check requires an enhancement (or equivalent allowed) label. This contributor account cannot add repository labels; a maintainer will need to apply it before the check can pass.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/modules/AdminModule.cpp`:
- Around line 978-987: Update the licensed first-region setup branch in
AdminModule.cpp after nodeDB->generateCryptoKeyPair() to persist all newly
generated identity state by saving SEGMENT_CONFIG | SEGMENT_MODULECONFIG |
SEGMENT_DEVICESTATE | SEGMENT_NODEDATABASE, ensuring the node number and owner
identity survive reboot.

In `@test/test_admin_radio/test_main.cpp`:
- Around line 968-976: In the test setup around testAdmin, preserve the existing
global owner and config.lora.region values before assigning test-specific
values, then restore both after the test completes, preferably with a small RAII
guard to cover failures and early exits. Use the NodeDB replacement block and
the licensed test setup as the integration point, ensuring restoration occurs
before teardown.

In `@test/test_packet_signing/test_main.cpp`:
- Around line 597-600: Shorten the Group C banner comment to one or two lines,
retaining only a concise section heading and the rationale that it covers
NodeInfoModule downgrade drops for broadcast ingress paths while unicast
NodeInfo is exempt.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7d239e62-5359-4d18-a5d7-5a318749999c

📥 Commits

Reviewing files that changed from the base of the PR and between 91043fa and 8009692.

📒 Files selected for processing (12)
  • src/main.cpp
  • src/mesh/Channels.cpp
  • src/mesh/NodeDB.cpp
  • src/mesh/NodeDB.h
  • src/mesh/Router.cpp
  • src/modules/AdminModule.cpp
  • src/modules/AdminModule.h
  • src/modules/NodeInfoModule.cpp
  • test/support/AdminModuleTestShim.h
  • test/support/MockMeshService.h
  • test/test_admin_radio/test_main.cpp
  • test/test_packet_signing/test_main.cpp

Comment thread src/modules/AdminModule.cpp
Comment thread test/test_admin_radio/test_main.cpp Outdated
Comment thread test/test_packet_signing/test_main.cpp
@RCGV1

RCGV1 commented Jul 10, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@RCGV1
RCGV1 marked this pull request as ready for review July 19, 2026 22:17
@vidplace7 vidplace7 added the enhancement New feature or request label Jul 19, 2026
@vidplace7 vidplace7 added the 2.8 label Jul 19, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/mesh/RadioInterface.h (1)

259-262: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Condense the comment to a maximum of two lines.

As per coding guidelines, code comments should be kept to a maximum of one or two lines. Please condense this explanatory block.

♻️ Proposed refactor
-    // Check if a candidate region is compatible and valid, with no side effects (safe for
-    // speculative UI checks). prospectiveLicensedOwner is for a UI flow that requires
-    // confirmation before it sets the owner licensed. errBuf, if given, receives the failure reason.
+    // Checks region compatibility without side effects. prospectiveLicensedOwner bypasses
+    // the license check for confirmation flows. errBuf receives the failure reason.
     static bool checkConfigRegion(const meshtastic_Config_LoRaConfig &loraConfig, char *errBuf = nullptr, size_t errLen = 0,
                                   bool prospectiveLicensedOwner = false);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mesh/RadioInterface.h` around lines 259 - 262, Condense the explanatory
comment above checkConfigRegion to no more than two lines while preserving its
key details about prospectiveLicensedOwner and errBuf failure reporting.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/graphics/draw/MenuHandler.cpp`:
- Around line 288-297: Safeguard the selectedRegionInfo access before reading
profile->licensedOnly by validating the region result and profile pointer,
preserving the existing invalid-region handling in
RadioInterface::checkConfigRegion. Initialize regionErr when declaring it so
failure logging never reads uninitialized data. Condense the adjacent three-line
explanatory comment to no more than two lines without changing the
prospective-owner validation behavior.

---

Nitpick comments:
In `@src/mesh/RadioInterface.h`:
- Around line 259-262: Condense the explanatory comment above checkConfigRegion
to no more than two lines while preserving its key details about
prospectiveLicensedOwner and errBuf failure reporting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c2d0e6aa-de53-4454-9b7f-6497cb0eca34

📥 Commits

Reviewing files that changed from the base of the PR and between 5e521cf and 446e146.

📒 Files selected for processing (4)
  • src/graphics/draw/MenuHandler.cpp
  • src/mesh/RadioInterface.cpp
  • src/mesh/RadioInterface.h
  • test/test_admin_radio/test_main.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/test_admin_radio/test_main.cpp

Comment thread src/graphics/draw/MenuHandler.cpp
@vidplace7

Copy link
Copy Markdown
Member

Tested. Confirmed this solves the issues with ham-mode causing nodes to regenerate their IDs upon every reboot.

Have not yet tested that this is still sending unencrypted messages 😬

@RCGV1

RCGV1 commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

Hardware verification: PASS

Verified PR head 6584e1e8d5b9ece848681b8efe9367e39fc71eed (2.8.0.6584e1e) on the physical test bench.

  • ESP32-S3: LilyGo T-Beam S3 Core
  • nRF52840: Muzi Base
  • Both nodes set to licensed KO6CNT; confirmed isLicensed=true and primary channel psk=unencrypted.
  • Refreshed NodeInfo on both nodes before traffic so each had the other's current public key.
  • T-Beam -> Muzi: acknowledged directed plaintext text delivered; Muzi logged Verified XEdDSA signature from 0x01274b75.
  • Muzi -> T-Beam: acknowledged directed plaintext text delivered; T-Beam logged Verified XEdDSA signature from 0x22ffe987.

This validates the PR's licensed-mode plaintext signing and receiver-side verification path in both directions across ESP32 and nRF52 hardware.

@thebentern
thebentern enabled auto-merge July 26, 2026 23:12
@vidplace7
vidplace7 disabled auto-merge July 26, 2026 23:14
@vidplace7
vidplace7 enabled auto-merge July 26, 2026 23:14
@vidplace7
vidplace7 added this pull request to the merge queue Jul 27, 2026
Merged via the queue into meshtastic:develop with commit 1e982fa Jul 27, 2026
100 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2.8 enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[2.8.0] Sign plaintext licensed/ham traffic

3 participants