Send Origin messages to one registered listener, signed, instead of broadcasting - #377
Draft
mullinmax wants to merge 2 commits into
Draft
Send Origin messages to one registered listener, signed, instead of broadcasting#377mullinmax wants to merge 2 commits into
mullinmax wants to merge 2 commits into
Conversation
…roadcasting Game events went out as plain JSON to 255.255.255.255:6809, so every board on the network shouted at every listener. That is enough broadcast traffic to jam the WiFi chip, and anything on the LAN could forge a score packet. A listener now registers over the authenticated /api/origin/target route, handing over a shared secret and, optionally, the address to send to -- when it is omitted the board uses the address the request arrived from, which is what a listener behind NAT needs. The board keeps the target as 4 raw IP bytes plus the secret in RAM, unicasts every game event there, and prefixes each datagram with 16 hex characters of HMAC-SHA256(secret, body). Bodies carry a counter that increments per send so a listener can drop replays; registering again rotates the secret and resets it. With nobody registered the board sends nothing at all -- there is no broadcast fallback. State is deliberately RAM-only: a reboot clears it and the listener re-registers, which is also how a moved or restarted listener recovers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmtVSp58rDkDBRPzL9hJkt
Contributor
|
Developer build links: Sys11 (Tiny) WPC EM WhiteStar DataEast Classic |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Game events (game state, end of game, reset) no longer go to the broadcast address. A listener registers itself over a new authenticated route and the board unicasts to that one address, signing every datagram.
New route —
POST /api/origin/target(authenticated), mirroring the shape of/api/memory/toggle-broadcast:{"enable": true, "secret": "…", "ip": "192.168.1.5"}ipis optional; when omitted the board usesrequest.client_ip. That is the case that matters — a listener behind NAT cannot name its own translated address, but the board sees it.{"enable": false}stops the stream.src/common/origin.pykeeps the target as 4 raw IP bytes plus the secret, and prefixes each datagram with 16 hex characters ofHMAC-SHA256(secret, body):The body gains
"n", a counter that increments per send, so a listener can drop replays. Registering again rotates the secret and resets the counter.Motivation and Context
Two problems with broadcasting:
HMAC on every datagram costs well under 0.5 ms on the RP2350 (
hashlib.sha256is native C) — roughly 0.2% duty cycle at 4 Hz, less than theujson.dumpsalready in the send path. Truncating the tag to 64 bits keeps the packet small; that is far past what a LAN attacker brute-forces within the life of a session secret.Target state is RAM-only by design: a reboot clears it and the listener re-registers, which is the same path that recovers a moved or restarted listener. No FRAM layout change, no stale target surviving a move.
Related Issues
Part of a three-repo change:
origin.pack()/unpack(),Machine.set_origin_target()Testing
black,isort,flake8clean (the twoF824warnings inbackend.pyare pre-existing).docs/routes.mdregenerated viatools/gen_api_docs.py.tests/test_origin.py) — both sides compute the tag over the same bytes.Types of Changes
On the breaking half: a board running this firmware sends nothing until a listener registers, so an older Origin that only listens for broadcasts will see no game events from it. The matching Origin change registers on startup and periodically thereafter.
Checklist
dev/testscovers build tooling only; there is no harness forsrc/common— framing is covered on the library side.)Generated by Claude Code