Skip to content

feat(security): surface XEdDSA packet signing in node & messaging UI#5976

Merged
jamesarich merged 1 commit into
mainfrom
feat/xeddsa-signing-ui
Jun 27, 2026
Merged

feat(security): surface XEdDSA packet signing in node & messaging UI#5976
jamesarich merged 1 commit into
mainfrom
feat/xeddsa-signing-ui

Conversation

@jamesarich

Copy link
Copy Markdown
Collaborator

Closes #5966 · Tracking design#113 · Firmware firmware#10478 (2.8)

Why

Firmware 2.8 signs unencrypted broadcasts with XEdDSA over the node's identity key and verifies them on the radio — invalid or stripped-signature broadcasts are dropped before the app sees them. So there's no "tampered" state to render; our job is purely to affirm the good state: show users when a node and its broadcasts are signed and verified. We never warn on unsigned traffic, because DMs, oversized broadcasts, and legacy nodes are all legitimately unsigned.

This is read-only — the radio does all crypto and hands the client three flags (MeshPacket.xeddsa_signed, NodeInfo.has_xeddsa_signed, Data.xeddsa_signature).

🌟 What changed

Messaging view — a subtle green shield-check on verified broadcast bubbles (next to the transport icon), plus a "Signed · verified / Verified with the sender's key." row in the message info sheet. The flag is only ever set on broadcasts, so it never appears on DMs — the encryption lock stays for DMs, the shield means authentic. Per-message, since a node can send a signed small + unsigned large message in one channel.

Node details — a "Signed node / Verified automatically" shield-check row in the security section, ordered most-trusted-first (manual-verify → signed → has-key). "Automatically" distinguishes radio-observed signing from user-asserted manual key verification.

Icon — new MeshtasticIcons.ShieldCheck (Material Symbols verified_user, authored in the repo's 960-viewport house style). Lock is untouched.

🛠️ How it's wired

  • Per-message: MeshPacket.xeddsa_signedDataPacketMessage, mirroring the existing transportMechanism plumbing. No DB migrationDataPacket is a serialized blob.
  • Node-level: NodeInfo.has_xeddsa_signedNode.signsPackets, persisted via a new NodeEntity column and a DB 43→44 auto-migration (44.json committed). Threaded through all three node→entity mappers.

Scope (deferred — track the open questions in design#113)

Out of v1: the "unverifiable signature" state (spec sanctions dropping it — "use only the xeddsa_signed bool"), per-data-field telemetry badges (open Q3), and a compose-time "will be signed" indicator (open Q5). Each is an isolated add-on.

🧪 Testing performed

  • ./gradlew spotlessCheck detekt test allTests — all green.
  • Added Compose Screenshot Tests for both surfaces (ScreenshotMessageItemSigned, ScreenshotNodeDetailsSectionSigned, light + dark); all 309 CST references pass. Reference PNGs committed under screenshot-tests/.

Note: signing flags populate only when connected to a 2.8 node that signs; this UI rides on the already-pinned protobufs 2.7.26-21879a9-SNAPSHOT, which carries all three fields.

🤖 Generated with Claude Code

Firmware 2.8 signs unencrypted broadcasts with XEdDSA and verifies them
before the app sees them. This affirms that good state to users — a node
and its broadcasts are cryptographically signed and verified — without
ever warning on legitimately-unsigned traffic (DMs, large broadcasts,
legacy nodes).

Read-only: the radio does all crypto and hands the client three flags.
- Per-message: surface MeshPacket.xeddsa_signed through DataPacket →
  Message (mirrors the existing transportMechanism plumbing; no DB
  migration — DataPacket is a serialized blob). A green shield-check
  shows on verified broadcast bubbles and a "Signed · verified" row in
  the message info sheet. Never shown on DMs (the radio doesn't set the
  flag there) — lock stays for DM encryption, shield for signing.
- Node-level: surface NodeInfo.has_xeddsa_signed onto the Node domain
  and persist it (NodeEntity column + DB 43→44 auto-migration). A
  "Signed node" shield-check row sits in the security section, ordered
  most-trusted-first (manual-verify → signed → has-key). "Verified
  automatically" distinguishes it from user-asserted manual verification.

Adds MeshtasticIcons.ShieldCheck (Material Symbols verified_user) and
CST preview screenshots for both surfaces (light/dark).

Deferred (track design#113 open questions): the "unverifiable signature"
state, per-data-field telemetry badges, and a compose-time indicator.

Closes #5966

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added the enhancement New feature or request label Jun 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📄 Docs staleness check — advisory

This PR modifies user-facing UI source files but does not update any page under docs/en/user/ or docs/en/developer/.

⚠️ Doc changes propagate to 3 consumers: in-app docs browser, Jekyll site (GitHub Pages), and meshtastic.org (Docusaurus sync). Updating a page in docs/en/ automatically flows to all three.

Changed source files:

core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/icon/Security.kt
feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/component/MessageActionsBottomSheet.kt
feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/component/MessageItem.kt
feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/component/NodeDetailsSection.kt

What to check:

Changed area Likely doc page
feature/messaging/ docs/en/user/messages-and-channels.md
feature/node/ docs/en/user/nodes.md or docs/en/user/node-metrics.md
feature/map/ docs/en/user/map-and-waypoints.md
feature/connections/ docs/en/user/connections.md
feature/settings/ docs/en/user/settings-radio-user.md or docs/en/user/settings-module-admin.md
feature/firmware/ docs/en/user/firmware.md
feature/intro/ docs/en/user/onboarding.md
feature/discovery/ docs/en/user/discovery.md
feature/docs/ Internal docs infrastructure
core/ui/ docs/en/developer/codebase.md or component-specific user pages

New page checklist (if adding a new doc page):

  1. Create the .md file in docs/en/user/ or docs/en/developer/ with last_updated frontmatter
  2. Register in DocBundleLoader.kt with string resources (in-app browser)
  3. Jekyll and Docusaurus sync pick up new pages automatically — no config change needed

If this PR does not require a doc update (e.g., internal refactor, bug fix, test change), add the skip-docs-check label to dismiss this check.

Cross-platform note: This check is advisory while doc coverage matures. Both Android and Apple repos use the same skip-docs-check label and advisory severity. See meshtastic/design standards for shared conventions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[2.8.0] - XEdDSA Public Key Signing

1 participant