Skip to content

Budget the admin-key PKI decrypt fallback#11100

Merged
caveman99 merged 3 commits into
developfrom
throttle-pki-admin-fallback
Jul 21, 2026
Merged

Budget the admin-key PKI decrypt fallback#11100
caveman99 merged 3 commits into
developfrom
throttle-pki-admin-fallback

Conversation

@caveman99

@caveman99 caveman99 commented Jul 20, 2026

Copy link
Copy Markdown
Member

Router::perhapsDecode tries each configured admin key when the sender's own key does not decrypt a PKI unicast. Each attempt is an X25519 scalar multiplication, and the AEAD tag that rejects a forged packet is only checked afterwards, so the expensive work happens before any authentication. An attacker needs a frame with channel=0, to set to the victim, 13+ bytes of random ciphertext and a fresh id; every such packet costs the full three operations because the loop only stops on success. Nothing on the path rate limited this.

p->from is unauthenticated header data, so a per-node throttle is evaded by varying it. The budget is therefore global: a token bucket of 8, refilling one per 250ms, spent only when the fallback actually runs.

Successful runs refund their token. A successful admin decrypt already persists the admin key into the sender's NodeDB entry, so subsequent packets from that admin take the existing single-operation fast path and never re-enter the loop. Authorized administration is low volume and unaffected.

Trying admin keys for senders that are not yet in NodeDB is deliberate, so that remote administration works against a node that has not learned the admin's key. That behavior is unchanged.

Adds a regression test covering both directions: the fallback stops running once the burst is drained, and it resumes after the bucket refills.

Summary by CodeRabbit

  • Bug Fixes
    • Added global throttling for PKI administrative-key fallback decryption to reduce flood impact.
    • Failed fallback attempts are now rate-limited, and successful decryption continues to work normally once the correct key is used after the limit refills.
  • Tests
    • Added a unit test validating rate-limited fallback behavior, including budget exhaustion, refill, and eventual success with the correct administrative key.

Each run costs up to three X25519 operations and runs before the AEAD tag
is checked, so unauthenticated unicasts can pin the CPU. p->from is
attacker-controlled, so the budget is global rather than per-node.

Successful runs refund their token, and a successful admin decrypt already
persists the sender key so later packets take the single-operation fast
path. Authorized administration is unaffected.
@caveman99 caveman99 added the bugfix Pull request that fixes bugs label Jul 20, 2026
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 978370b5-1652-4ae8-9e3b-15dd5fb71275

📥 Commits

Reviewing files that changed from the base of the PR and between 549c9fe and 1538159.

📒 Files selected for processing (2)
  • src/mesh/Router.cpp
  • test/test_pki_admin_fallback/test_main.cpp

📝 Walkthrough

Walkthrough

PKI admin-key fallback decryption now uses a global token-bucket limiter with refill and success refunds. Tests cover budget exhaustion, refill, successful decoding, sender-key persistence, and test registration.

Changes

PKI admin-key fallback rate limiting

Layer / File(s) Summary
Fallback token-bucket budget
src/mesh/Router.cpp
Adds compile-guarded global token-bucket state and helpers for refill, token spending, and refunds.
Fallback integration and validation
src/mesh/Router.cpp, test/test_pki_admin_fallback/test_main.cpp
Gates admin-key decryption attempts, refunds successful fallbacks, and tests budget exhaustion, refill, sender-key persistence, and test registration.

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

Sequence Diagram(s)

sequenceDiagram
  participant perhapsDecode
  participant FallbackBudget
  participant AdminKeyDecryptor
  perhapsDecode->>FallbackBudget: Request fallback token
  FallbackBudget-->>perhapsDecode: Allow or deny attempt
  perhapsDecode->>AdminKeyDecryptor: Try configured admin keys
  AdminKeyDecryptor-->>perhapsDecode: Return decryption result
  perhapsDecode->>FallbackBudget: Refund token after successful fallback
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the change well, but it omits the required attestations/testing checklist from the template. Add the attestation checklist and indicate what devices or environments were tested, including any untested hardware/regression caveats.
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% 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 is concise and accurately reflects the main change: rate-limiting the admin-key PKI decrypt fallback.
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 throttle-pki-admin-fallback

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.

🧹 Nitpick comments (1)
src/mesh/Router.cpp (1)

555-558: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Trim duplicated multi-line rationale comments to one line each. Both sites restate the same budget-rationale explanation across 3-4 lines, exceeding the repo's comment-length limit.

  • src/mesh/Router.cpp#L555-L558: condense to a single line (e.g., note only that the budget is global because p->from is attacker-controlled and refunds on success).
  • test/test_pki_admin_fallback/test_main.cpp#L205-L207: condense to a single line; the full rationale is already documented at the implementation site in Router.cpp.

As per coding guidelines, "Keep code comments minimal—one or two lines maximum—and comment only when the reason is not obvious; do not restate straightforward code or add multi-paragraph explanatory blocks."

🤖 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/Router.cpp` around lines 555 - 558, Condense the duplicated
multi-line rationale comments to one line at src/mesh/Router.cpp lines 555-558
and test/test_pki_admin_fallback/test_main.cpp lines 205-207. In Router.cpp,
retain only that the budget is global because p->from is attacker-controlled and
successful runs refund the token; in the test file, use a brief reference to the
implementation rationale without repeating it.

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.

Nitpick comments:
In `@src/mesh/Router.cpp`:
- Around line 555-558: Condense the duplicated multi-line rationale comments to
one line at src/mesh/Router.cpp lines 555-558 and
test/test_pki_admin_fallback/test_main.cpp lines 205-207. In Router.cpp, retain
only that the budget is global because p->from is attacker-controlled and
successful runs refund the token; in the test file, use a brief reference to the
implementation rationale without repeating it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7f8b1b0d-dd07-4ab9-80d6-280c9303eb05

📥 Commits

Reviewing files that changed from the base of the PR and between 0199a1f and 1bc06f6.

📒 Files selected for processing (2)
  • src/mesh/Router.cpp
  • test/test_pki_admin_fallback/test_main.cpp

@caveman99

Copy link
Copy Markdown
Member Author

Condensed both comment sites. The rationale now lives in one line at the implementation and the test refers to it rather than repeating it. Tests still pass, 5 cases.

@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 549c9fe.

# Conflicts:
#	test/test_pki_admin_fallback/test_main.cpp
@caveman99
caveman99 merged commit 7bdc256 into develop Jul 21, 2026
22 of 23 checks passed
@thebentern
thebentern deleted the throttle-pki-admin-fallback branch July 21, 2026 10:54
thebentern added a commit to RCGV1/firmware-Fork that referenced this pull request Jul 21, 2026
Resolve conflicts against the NodeDB signer/key primitives (meshtastic#11050) and the
admin-key PKI decrypt budget (meshtastic#11100).

- NodeDB: drop this branch's hasSeenXeddsaSigner in favour of develop's
  isKnownXeddsaSigner. They answer the same question, but develop's reads the
  dedicated warm signer bit (warmSignerOf) rather than the WarmProtected
  category, and TrafficManagementModule already depends on it. Keep develop's
  copyPublicKey/copyPublicKeyAuthoritative, isVerifiedSignerForKey and
  commitRemoteKey/KeyCommitTrust.
- checkXeddsaReceivePolicy: keep this branch's Strict/Balanced/Compatible
  policy, which is a superset of develop's balanced-only downgrade gate, and
  call isKnownXeddsaSigner from it. develop's !pki_encrypted term is dropped
  because the policy returns early for PKI packets before that check.
- perhapsDecode: keep develop's key resolution (NodeDB then pending-key, only
  for real PKI candidates) plus its admin-key token bucket, and re-apply this
  branch's pkiAttempted flag feeding the DECODE_OPAQUE verdict. Keep both
  passesRoutingAuthGate and adminKeyFallbackAllowed/Refund.
- test_A17: model eviction the way NodeDB actually does it, passing the warm
  signer bit as well as the XeddsaSigner category, since isKnownXeddsaSigner
  reads the former.

Native suite: 38 suites, 743/743 cases, no sanitizer findings.
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