Experiment with using construct for packet parsing - #61
Conversation
|
Since this PR is rebuilding the packet layer, here are three findings from our capture work that bear directly on the design while it's still cheap to change. All of it comes from four PacketLogger captures (A91B2 / A2345 / C2000 G2) cross-checked against the iOS app's own debug logs, which conveniently log both the session key and the pre-encryption plaintext of every frame. 1. The device tells you which negotiation path it speaks — before you connectThe manufacturer data in the advert (company id
The capability byte is an 8-bit mask ( That last line is byte-identical to The device then repeats the same fact on the wire in its stage-2 response, so a client never strictly needs the advert:
The payload is otherwise byte-identical across families — The practical upshot: the One caveat worth knowing: the app reports capability per 2.
|
|
@kb1ibt I appreciate your input on this, I have not tested your solution to the fragmentation problem yet but its something I plan on looking into. With the type thing I have not decided what to do about it yet, my current plan is to make it so that parsed packets do not use it (it all goes to value) but you can build a packet using it and its automatically added. The information about the supported negotiation paths is interesting, but I don't think its worth implementing yet. In terms of #48 I plan on merging this instead, my prior implementation of packet parsing and building left a lot to be desired as it was created without much understanding of the protocol, so rather than build upon that mess I made this to replace it and then implement more dynamic features like key generation, timestamps, and timezones on top of it, which should also make it easier to add in support for devices using more exotic negotiations (like ones which need the cloud-id). |
|
Since this replaces the packet layer wholesale, one structural finding is worth having before the schemas harden — it's the thing most likely to force a rework later. TLV field tags are ordinal per opcode, not a global vocabulary. A tag is "the Nth field of this message", and its meaning is only defined relative to the opcode. The same tag means unrelated things in different messages:
Note the module firmware moves from Two corollaries that bite in practice:
All of the above is from wire captures (PacketLogger + the app's own debug logs, cross-checked against live devices) — happy to paste the specific frames for any of it if that's useful while you're shaping the schemas. |
|
Two more things for the packet layer, both from live A2345 captures this week. There is a third frame pattern: …then discards it. On an A2345 that discarded frame is the grant that arms telemetry: after a Timezone/timestamp specifics, since dynamic generation is this PR's goal:
On fragmentation, in case the rewrite touches the cap: the device declares its own frame cap as field 2 of the stage-2 response ( |
|
One more structure worth pulling into
What Three things fall out that the library could use regardless:
That last one matters more than it looks, because device-class resolution currently keys on the advertised name, and a unit within ~5 minutes of its first factory boot has no name at all. I scanned a sealed A2345 immediately after first power-on: Also worth noting for any MAC-prefix matching: current-production hardware is on All five adverts above are raw passive-scan captures — I can paste the byte strings if you want them as test vectors for the parser. |
This PR aims to implement dynamic negotiation, this means dynamically generating a public/private key, timestamps, and time-zones instead of using statically defined ones, while this could be implemented without overhauling how packets are encoded and decoded, I think its about time to switch to a more concrete approach.
As it stands this PR currently contains my experiments with using construct to replace the previous packet decoding system and from this it seems that construct is definitely the right way to go, future work will be done in this PR to switch the rest of the code over and then implement the dynamic negotiation features.
That being said I think I will leave device._data alone for now, since changing that would require updating all of the devices and I have created a method to convert the new structures to the legacy ones.