Skip to content

Pin admin responses to the stored peer key and request id#11092

Open
caveman99 wants to merge 3 commits into
developfrom
admin-response-pin
Open

Pin admin responses to the stored peer key and request id#11092
caveman99 wants to merge 3 commits into
developfrom
admin-response-pin

Conversation

@caveman99

@caveman99 caveman99 commented Jul 20, 2026

Copy link
Copy Markdown
Member

AdminModule::noteOutgoingAdminRequest computed its PKC pin as p.pki_encrypted && p.public_key.size == 32. Neither field is populated on the outgoing path: the only firmware write to MeshPacket.public_key is in perhapsDecode (RX), and pki_encrypted is set in perhapsEncode, after MeshService::handleToRadio has already called this. So keyValid was false for every client request and the pin was dead code.

The accepted-response predicate therefore reduced to from == queried node, plus variant, subtype, window and consumption. from is unauthenticated, so an attacker who knows an operator has an outstanding get_module_config_request(REMOTEHARDWARE) could inject a matching response over RF or MQTT downlink and overwrite node_remote_hardware_pins[].

Changes:

  • Resolve the destination key with nodeDB->copyPublicKey (what perhapsEncode encrypts to) and pin that.
  • Pin only when the request would actually go out over PKC, so ham mode, a missing private key, or the serial/gpio channels do not cause legitimate plaintext responses to be rejected. That condition is extracted from perhapsEncode as wouldEncryptWithPKC and shared, rather than duplicated.
  • Record the request's packet id and require the response to echo it as decoded.request_id. setReplyTo already sets this on every reply.

Tests: the previous pin test constructed pki_encrypted/public_key by hand, which the outgoing path never produces, so it passed against a synthetic input. It now uses a NodeDB-stored key. Added coverage for the stored-key pin, the request-id binding, and a ham-mode case asserting an unpinnable request is not rejected.

Summary by CodeRabbit

  • Security

    • Improved protection for administrative responses by matching them to the originating request via an echoed request identifier.
    • Strengthened encrypted admin destination-key pinning to use the destination’s resolved public key and avoid pinning from incomplete outgoing data.
  • Bug Fixes

    • Prevented incorrect pinning/encryption decisions for unsupported modes (including ham-mode).
    • Ensured responses with missing or mismatched request identifiers are ignored.
  • Tests

    • Expanded and stabilized coverage for request/response correlation, destination-key pinning, ham-mode behavior, and rejection of incorrect or zero identifiers.

noteOutgoingAdminRequest derived its PKC pin from p.public_key, which nothing
populates on the outgoing path, so keyValid was false for every client request
and the pin never engaged. The accepted-response predicate reduced to an
unauthenticated from plus variant and subtype.

Resolve the destination key from NodeDB the way perhapsEncode does, and pin it
only when the request would actually be PKC-encrypted. Extract that condition
from perhapsEncode as wouldEncryptWithPKC so both use one predicate. Also
record the request's packet id and require the response to echo it.
@coderabbitai

coderabbitai Bot commented Jul 20, 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: b917ba2d-d08a-41da-8bf5-3d6796061db8

📥 Commits

Reviewing files that changed from the base of the PR and between 7c26cf1 and 04f486f.

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

📝 Walkthrough

Walkthrough

PKC eligibility is centralized in a Router helper. Admin requests now record packet IDs and destination keys, while responses require matching request IDs. Tests cover stored-key pinning, ham mode, unpinned requests, and mismatched responses.

Changes

Admin PKC correlation

Layer / File(s) Summary
PKC eligibility selection
src/mesh/Router.h, src/mesh/Router.cpp
PKC eligibility checks are centralized in wouldEncryptWithPKC(), and perhapsEncode() uses it to select PKC or channel encryption.
Admin request tracking and validation
src/modules/AdminModule.h, src/modules/AdminModule.cpp
Outstanding requests store packet IDs and destination keys; responses must match the stored request ID and existing validation constraints.
Admin pinning and correlation tests
test/test_admin_session_repro/test_main.cpp
Tests cover stored-key pinning, unpinned and ham-mode requests, zero-ID handling, and rejection of incorrect request IDs.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AdminModule
  participant NodeDB
  participant Router
  participant AdminResponse
  AdminModule->>NodeDB: retrieve destination public key
  AdminModule->>Router: evaluate PKC eligibility
  AdminModule->>AdminModule: store requestId and pinned key
  AdminResponse->>AdminModule: return request_id and response
  AdminModule-->>AdminResponse: accept matching validated response
Loading

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: thebentern

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% 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 matches the main change: admin response pinning to stored peer keys and request IDs.
Description check ✅ Passed The description covers the bug, changes, and tests, but it omits the repository's attestations/checklist section.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch admin-response-pin

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.

@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

🤖 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`:
- Line 1934: Guard the request ID assignment in the request-slot setup around
slot->requestId and responseIsSolicited: treat p.id == 0 as having no valid
pinning token, and ensure zero can never satisfy the later decoded request_id
comparison. Preserve normal nonzero request IDs and the existing
solicited-response behavior.
🪄 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: 3b80af4c-465e-4733-95b9-e129127e1e45

📥 Commits

Reviewing files that changed from the base of the PR and between 290967f and 958ccba.

📒 Files selected for processing (5)
  • src/mesh/Router.cpp
  • src/mesh/Router.h
  • src/modules/AdminModule.cpp
  • src/modules/AdminModule.h
  • test/test_admin_session_repro/test_main.cpp

Comment thread src/modules/AdminModule.cpp
@github-actions

github-actions Bot commented Jul 20, 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-19. Updated for 7c26cf1.

@caveman99 caveman99 added the bugfix Pull request that fixes bugs label Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Pull request that fixes bugs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant