Skip to content

Answer a stale EAP-AKA SQN with a synchronization failure - #25

Merged
ps2 merged 1 commit into
mainfrom
eap-sqn-resync
Oct 26, 2026
Merged

ps2 merged 1 commit into
mainfrom
eap-sqn-resync

Conversation

@ps2

@ps2 ps2 commented Oct 26, 2026

Copy link
Copy Markdown

The simulator accepted any SQN a controller sent. The session keys don't depend on it, and AUTN wasn't checked, so a controller whose SQN had fallen behind connected as if nothing had happened. A real pod answers that with a synchronization failure, which is how a controller learns another controller has had sessions with the pod since its last. That path in OmnipodKit, and the Sport Mode work that relies on it (loopandlearn/OmnipodKit#177), never ran in the Simulator.

Now the pod:

  • reads the controller's SQN from AUTN and verifies MAC-A, hanging up if it doesn't verify;
  • answers an SQN that isn't past the last one it accepted with a synchronization failure whose AUTS carries its own SQN, then waits for the controller's retry on the same connection, as OmnipodKit's establishNewSession does it;
  • stores the controller's SQN, starting from 0 at pairing (the controller's first session uses 1).

One resync on an existing pod. The simulator used to store one more than the controller's SQN, so a pod paired before this change resyncs once at its next session, and OmnipodKit logs that. A pod paired after it doesn't.

Testing

  • New tests drive a session over the test radio: a stale SQN gets a synchronization failure carrying the pod's SQN with a valid MAC-S, and the retry with the next SQN establishes the session; an AUTN that doesn't verify hangs up. go test ./... passes.
  • Checked against OmnipodKit's own Milenage and EapMessage in a throwaway OmniTests case: same AUTN for the controller's SQN, and the simulator's synchronization failure parses, decodes to the pod's SQN and passes the MAC-S check.
  • Not yet run against Loop in the Simulator.

The pod took any SQN a controller sent: the session keys don't depend
on it and AUTN wasn't checked. So a controller whose SQN had fallen
behind, because another controller had sessions with the pod since its
last, connected as if nothing had happened, and OmnipodKit's
resynchronization path never ran in the Simulator.

The pod now reads the controller's SQN from AUTN, verifies MAC-A, and
answers an SQN that isn't past the last one it accepted with a
synchronization failure: AUTS carries its own SQN, as a real pod's does.
It then waits for the retry on the same connection. An AUTN that doesn't
verify hangs up. The pod stores the controller's SQN, starting from 0 at
pairing, since the controller's first session uses 1.
jeremybarnum pushed a commit to jeremybarnum/sportmode-sim that referenced this pull request Oct 26, 2026
The pod holds the phone's bolus while the phone leaves its reach, so the pod gives it and the answer
is lost; the watch starts without the phone. Back, the phone stands aside, its pod driver drops the
unconfirmed command at the resync and lets go, and the bolus is booked once. Needs a pod simulator
that resyncs (LoopKit/pod#25 through POD_SIM), so it is in no tier yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ps2
ps2 marked this pull request as ready for review October 26, 2026 21:08
@ps2
ps2 merged commit eb48d87 into main Oct 26, 2026
1 check passed
@ps2

ps2 commented Oct 26, 2026

Copy link
Copy Markdown
Author

For the record, a real DASH pod behaves the way this models it. On Sport Mode (Jeremy's next-dev-SportMode-PRs build) the watch held the pod for about 17 minutes, then the phone took it back:

21:34:11 Pod connected
21:34:12 [trust] EAP SQN RESYNC — pod=10 ours=7: another controller may have run this shared pod
21:34:12 send 17732cfd30030e010781ed
21:34:13 receive 17732cfd340a1d28001e3800000167ff0001

The pod answered the phone's stale SQN 7 with its last accepted SQN, 10, not 11 (the simulator's lastSqn). The phone's retry with 11 on the same connection was accepted, and commands went through right after. The report's pod state ends with eapSeq: 11.

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