Pre-release hardening 1-3: nonce prefix widen + status-text dedup + stale comments

From the four-agent pre-release review (all read-only), the three items that warranted
fixing before ship:

1. NONCE PREFIX WIDENED (the one security-relevant finding). The interim counter-nonce
   used a 32-bit random prefix + 64-bit counter; since a fresh instance restarts the
   counter at 0, two instances that drew the same prefix would reuse nonces under the
   same long-lived key (catastrophic for AES-GCM), and 32 bits collides at only ~2^16
   instances. Now 48-bit random prefix + 48-bit counter: birthday bound ~2^24 instances
   while 2^48 packets/session stays far beyond any real session - strictly safer than
   both the interim scheme AND the original per-packet 96-bit-random nonce. Wire
   unchanged (receiver reads the nonce off the packet). Comment corrected.

2. STATUS-TEXT DE-DUPLICATED. The CheckedListBox spoken-status builder existed in two
   copies (CheckedListAccessibility + MainForm) with a comment falsely claiming they were
   "exact" - and they had already drifted (the remembered-apps empty-state line was in the
   MainForm copy only). Folded to ONE builder (CheckedListAccessibility.ApplyStatus +
   EmptyTextFor); MainForm delegates. NVDA wording is now identical in the main window and
   every dialog by construction. No user-visible change today; removes the silent-desync trap.

3. STALE COMMENTS. RemPacket still documented the pre-5.6 plaintext control wire format
   (2 bytes / 14 on the wire); it is now sealed (50 on the wire, the 2 bytes are inner
   plaintext). Fixed that and the MainForm.Peers.cs comment that misplaced CaptureSpecBuilder
   in Core (it is App by necessity). Docs only.

Plus review-flagged test top-ups: NonceSequence layout + two-instance-distinct-prefix
assert; ControlReceiveGuard future-dated rejection + 300-command burst (no false replay
across the prune threshold); log-rotation INTEGRITY (every line survives exactly once)
+ a 4-thread concurrency test that actually fails if writeGate is removed; ReleaseSigning
now honestly SKIPs (not caveat-PASSes) when the publisher key is absent.

Gate 69/69.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-07-27 09:57:25 +01:00
co-authored by Claude Fable 5
parent 6c53fe54d1
commit 26a6fbfdf6
6 changed files with 150 additions and 56 deletions
+4 -2
View File
@@ -12,8 +12,10 @@ namespace RemSound.App;
/// The PEER half of the main window — state, discovery/selection reconciliation, arming, naming and
/// the remembered-peers plumbing — split out of MainForm.cs verbatim in the 2026-07-26 review's
/// god-object shrink (same partial-class pattern Andre's SensorReadout form uses). Pure code motion:
/// same class, same members, no behaviour change — the compiler proves it. The shared LOGIC these
/// methods lean on (PeerArming, CaptureSpecBuilder, PeerAddress) already lives in Core.
/// same class, same members, no behaviour change — the compiler proves it. The shared arming/address
/// LOGIC these methods lean on (PeerArming, PeerAddress) lives in Core; the send-spec builder
/// (CaptureSpecBuilder) stays in App by necessity — it depends on App's AudioDefaultFollower and the
/// Sender's app enumerator — but is likewise shared with the service.
/// </summary>
public sealed partial class MainForm
{