Files
franklinwh-agate-client/franklinwh_agate_client/protocol/__init__.py
T
kfj001 a87dbe200e Add recovered protocol stack, Wi-Fi hotspot join, and RE findings
Reconstruct the app's Dart-layer behaviour (blutter over libapp.so,
jadx over base.apk — see docs/RE_DART_FINDINGS.md) as working Python,
replacing the library stubs with a real local-connect stack.

protocol/ (new package, hardware-independent):
- commands: CommunicationMode / CommunicationOperate / CommunicationCmd
  enums with the raw values recovered from the Dart object pool
- envelope: CommunicationObject with the app's exact JSON key order
- crypto: SN-derived secret key and the position-dependent additive
  cipher (encrypt/decrypt; radix 16 default, 32 documented alternate)
- crc: CRC-16/MODBUS with the app's hi/lo swap + 4-hex string form,
  CRC32 envelope check, little-endian int16 helpers
- framing: BLE frame build/parse (0x5AA5 data / 0x4AA4 ACK magics,
  CRC slots, MTU chunking) and local-socket int16LE length framing
  plus a streaming SocketFrameDecoder

wifi.py (rewritten):
- derive_hotspot_credentials(): the AP_<sn> SSID/password rules
  (20-char SN branch, full-SN fallback with the app's warning log)
- WifiBackend abstraction: NmcliWifiBackend for Linux hosts via
  nmcli, NoOpWifiBackend for radio-less containers
- WifiManager keeps the original stub method names for API compat
  and adds join_device_hotspot(sn) / leave_device_hotspot() / local_ip()

device_local.py (new): LocalDeviceClient for the mode==local path —
the /terminal/* HTTP endpoint family (hotspot info, aGate info,
overall info, MQTT relay, ...) and direct-connect socket RPC
(envelope -> cipher -> length frame -> decrypt -> JSON) with
convenience query_* RPCs.

docs/RE_DART_FINDINGS.md (new): the full findings — transport
selector, wire envelope, crypto, CRCs, MQTT (AWS IoT + Cognito),
socket, BLE + ESP provisioning, hotspot credential derivation,
local/cloud HTTP layers, and the Java glue — plus the open items;
README now links to it.

Unconfirmed candidates (local port 18000, cipher key radix, BLE
field order, X-Device-* header values) are flagged in the docs and
kept configurable in code.
2026-10-13 00:18:23 +00:00

79 lines
1.8 KiB
Python

"""Pure-protocol building blocks recovered from the app's Dart AOT snapshot.
Everything here is hardware-independent and covered by unit tests. See
``docs/RE_DART_FINDINGS.md`` for the reverse-engineering evidence.
"""
from .commands import CommunicationCmd, CommunicationMode, CommunicationOperate
from .crc import (
CRC16_INIT,
CRC16_POLY,
crc16_hex_string,
crc16_modbus,
crc32,
int16_to_le_bytes,
le_bytes_to_int16,
)
from .crypto import (
ALT_KEY_RADIX,
DEFAULT_KEY_RADIX,
decrypt_object,
encrypt_object,
generate_secret_key,
header_len_for,
split_envelope,
)
from .envelope import KEY_ORDER, PLACEHOLDER, CommunicationObject, direct_connect_request
from .framing import (
MAGIC_ACK,
MAGIC_DATA,
build_ble_ack_frame,
build_ble_data_frame,
chunk_for_mtu,
content_in_data,
create_ble_frame,
frame_socket_payload,
magicEquals,
magic_in_data,
sn_in_data,
verify_frame_crc,
)
from .framing import SocketFrameDecoder
__all__ = [
"CommunicationCmd",
"CommunicationMode",
"CommunicationOperate",
"CRC16_INIT",
"CRC16_POLY",
"crc16_modbus",
"crc16_hex_string",
"crc32",
"int16_to_le_bytes",
"le_bytes_to_int16",
"DEFAULT_KEY_RADIX",
"ALT_KEY_RADIX",
"generate_secret_key",
"split_envelope",
"encrypt_object",
"decrypt_object",
"header_len_for",
"KEY_ORDER",
"PLACEHOLDER",
"CommunicationObject",
"direct_connect_request",
"MAGIC_ACK",
"MAGIC_DATA",
"create_ble_frame",
"build_ble_data_frame",
"build_ble_ack_frame",
"magic_in_data",
"magicEquals",
"sn_in_data",
"content_in_data",
"verify_frame_crc",
"chunk_for_mtu",
"frame_socket_payload",
"SocketFrameDecoder",
]