Skip to content

feat(spot): TLS for PSK Reporter with manual approval of an untrusted certificate (FR-SPOT-13) - #197

Merged
dc0sk merged 1 commit into
mainfrom
feat/spot-tls
Sep 21, 2026
Merged

dc0sk merged 1 commit into
mainfrom
feat/spot-tls

Conversation

@dc0sk

@dc0sk dc0sk commented Sep 21, 2026

Copy link
Copy Markdown
Owner

Stacked on #196 (POTA); the diff here is TLS only.

What

  • TLS switch for PSK Reporter (off by default; the port follows it, 1883 ↔ 1884).
  • Untrusted certificate approval. The server's certificate is verified against the public authorities first. If it fails for a reason about the certificate (unknown issuer, self-signed, expired, wrong name) the connection is refused, nothing is sent, and the prompt shows the reason and the SHA-256 fingerprint. Trust this certificate approves exactly that one.
  • Approvals are kept in the settings, listed, and can be withdrawn.

Design (mine — please overrule)

  • Exact: one host, one port, one certificate. A different certificate where one was approved is reported as changed with a warning, never accepted silently.
  • A fingerprint is not a password: an approved certificate still has to prove its private key (the handshake signature is verified on TLS 1.2 and 1.3). Tested with a public certificate replayed with the wrong key.
  • Only certificate problems are approvable, never a protocol or signature failure.
  • A click approves only the certificate that is on screen.
  • Asking for TLS never falls back to plain text.

Findings

  • The real server needs no approval. One TLS handshake with mqtt.pskreporter.info:1884 (nothing sent after it) shows its certificate is trusted by the public authorities. Approval is a fallback for a self-hosted broker or a filtering proxy.
  • openssl req -x509 marks a self-signed certificate as an authority, so rustls refuses it as CaUsedAsEndEntity before looking at the issuer. That is probably the most common real self-signed case; it now has proper wording.
  • My structural wiring guard matched its own source text and stayed green with the real line deleted; fixed.
  • No new crates: rustls, webpki-roots and sha2 were already in the tree (the lock file gains three dependency edges).

Verified

fmt, clippy --all-features, full workspace tests (432, bounded), cargo xtask OK. Real TLS on loopback with two throwaway certificates whose fingerprints were computed by openssl. Every new test mutated; survivors that were real gaps are listed in the ledger.

Validation parked with DC0SK (not done here)

  • Enable PSK Reporter + TLS against the real server and confirm it connects without a prompt. — Confirmed 2026-09-22 on the live K4: real spots draw as blue nameplates and click-to-tune lands on them correctly, no approval prompt (the real server's certificate is publicly trusted, as expected).
  • See the approval prompt on screen: point PSK Reporter's host at a broker with a self-signed certificate, check the wording, fingerprint layout and the Trust this certificate / Forget flow, and that a changed certificate shows the warning. — Confirmed 2026-09-22 against two throwaway local TLS servers with different self-signed certificates (P-256, generated for this check): the prompt appeared with the correct fingerprint, Trust this certificate connected successfully, and swapping to the second certificate on the same host:port produced the changed-certificate warning rather than a silent re-approval.

Not covered

RBN and DX cluster (plain telnet); POTA's HTTPS uses the standard checks with no approval option; the prompt shows reason and fingerprint but not the certificate's subject, issuer or dates; withdrawing an approval applies from the next connection.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ma4bQKqGNFTYyVSEKPcXRk

… certificate (FR-SPOT-13)

- k4-spot::mqtt_source: a Connector (plain TCP by default), a structured
  CertInfo / ConnectError::Untrusted, pending_cert and retry_now. Asking for
  TLS is never quietly downgraded to plain text.
- app/src/tls: rustls with a verifier that checks against the public
  authorities first; a certificate refused for a reason about the certificate
  is reported with its SHA-256 fingerprint and the reason, and nothing is sent
  to the server. An approval is exact (host, port, certificate), a changed
  certificate is reported as changed, and the handshake signature is still
  verified, so a public certificate replayed without its key is refused.
- k4-config: PskReporterPrefs.tls and TrustedCert (validated on read, capped).
- Networks: a TLS switch, an approval prompt and a list of approved
  certificates. A click approves only the certificate that is on screen.

The real mqtt.pskreporter.info:1884 presents a publicly trusted certificate
(observed once), so approval is a fallback on the real service.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ma4bQKqGNFTYyVSEKPcXRk
@dc0sk
dc0sk changed the base branch from feat/spot-more-networks to main September 21, 2026 18:00
@dc0sk dc0sk closed this Sep 21, 2026
@dc0sk dc0sk reopened this Sep 21, 2026
@dc0sk
dc0sk merged commit 34dd809 into main Sep 21, 2026
5 checks passed
@dc0sk
dc0sk deleted the feat/spot-tls branch September 22, 2026 08:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant