Skip to content

Sign plaintext packets in licensed mode#10969

Open
RCGV1 wants to merge 8 commits into
meshtastic:developfrom
RCGV1:codex/ham-mode-signing
Open

Sign plaintext packets in licensed mode#10969
RCGV1 wants to merge 8 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 migrations.
    • Extended LoRa region validation to support “prospective licensed owner” (including region-picker compatibility checks).
  • Bug Fixes

    • Improved licensed-channel sanitization and persistence across owner/config changes, boot defense, and preference restores (now persists only when needed and remains idempotent).
    • Tightened licensed PKI behavior and updated signing rules for licensed/broadcast traffic.
    • Safer preservation/regeneration of the security private key during installs and licensed identity updates.
  • Reliability

    • Licensed-operation cleanup and related state saving now happen only when required to avoid 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: 685029f3-863a-4c45-b919-1e1e816b693c

📥 Commits

Reviewing files that changed from the base of the PR and between 446e146 and 6584e1e.

📒 Files selected for processing (1)
  • src/graphics/draw/MenuHandler.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/graphics/draw/MenuHandler.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
Licensed packets are signed when their encoded data fits, PKI decryption is disabled for licensed nodes, and NodeInfo retains the public identity key.
Licensed identity migration lifecycle
src/mesh/NodeDB.*, src/modules/AdminModule.*, src/main.cpp, test/support/MockMeshService.h
Identity generation, migration detection, deferred notifications, region and ham-mode handling, and conditional persistence are added for licensed operation.
Licensed channel sanitization and persistence
src/mesh/Channels.cpp, src/mesh/NodeDB.cpp, src/modules/AdminModule.cpp
Admin and encrypted channels are sanitized during owner changes, boot migration, and preference restoration, with affected channel segments persisted.
Region validation and licensed behavior tests
src/mesh/RadioInterface.*, src/graphics/draw/MenuHandler.cpp, test/test_admin_radio/*, test/test_packet_signing/*, test/support/AdminModuleTestShim.h
Prospective licensed region validation is supported, and tests cover signing, PKI exclusion, identity handling, NodeInfo publication, channel sanitation, persistence, and test lifecycle support.

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/firmware#10966 — Directly covers the licensed plaintext signing, identity migration, NodeInfo publication, and PKI exclusion implemented here.
  • meshtastic/design#122 — Defines the licensed identity-key publication, plaintext signing, and PKI exclusion behavior implemented here.
  • meshtastic/design#123 — Covers overlapping licensed plaintext signing and identity handling, but not the remote Admin authorization changes.

Possibly related PRs

Suggested labels: module, mesh

Suggested reviewers: vidplace7, jp-bennett, thebentern

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: signing plaintext packets in licensed mode.
Description check ✅ Passed The description covers summary, validation, hardware status, and related references, though it omits the template's attestation checklist.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 (30)
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
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-18. Updated for 6584e1e.

@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 Outdated
@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 😬

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

2 participants