Repository navigation
feat(cli): add check-key to diagnose a device's key setup - #57
Conversation
|
Rough draft; didn't do any human review other than basic testing. Main idea: if your packets don't reach the backend, this tool will tell you what is wrong with your keys (incorrect size, incorrect encryption type, time out-of-sync). Potential future nice-to-have is a satellite scan through a connected SDR as well, to check satellite packet keys |
|
I thought I had already done something similar, but only parts of this have an overlap - the key sweep logic. I asked Claude to diagnose the overlap:
|
decrypt_eax derived key_0 once, at counter 0. The EID derivation takes key_0 from the counter's high 16 bits, so every counter of 65536 or more was compared against the wrong EIDs and never matched. The pool reaches 65536 once the period is 2^10 s or more. At the cloud's default exponent 15, that is pool slot 2, about 18 hours of uptime. From then on, `ble scan --key` and `ble detect` reported a correctly keyed AES-EAX device as undecodable. key_0 is now derived for each 65536-counter block, as _generate_eid does, and cached within the sweep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two things stopped the suite from running on Windows: - TestReadmeStaysTrue and the source scans in test_terminal_compat read files with read_text() and no encoding, which is cp1252 on Windows. The README's UTF-8 failed to decode. They now read UTF-8 explicitly. - test_terminal_compat imported pty at module level. pty needs termios, which Windows lacks, so the whole file failed to collect. The import now happens inside the one pty test, which skips where pty is missing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
0406791 to
d3cab2f
Compare
New patch combines this tool, now |
Adds synchronous cloud decryption, so nearby packets can be attributed
to devices without holding their keys.
- cloud.decode_packets: one POST /org/{id}/packets/decode call of up to
25 advertisements. A 403 names the missing read-packets scope.
- Organization.decode_packets: takes any number of AES-CTR/AES-EAX
packets, dedupes identical advertisements, batches them, and returns
results aligned to the input (None where the cloud could not decrypt).
Packets outside the endpoint's 72h/2h window are answered None locally,
since one invalid item fails the whole batch. Also adds get_device().
- AesEaxPacket.raw keeps the service data byte for byte, as the endpoint
needs it.
- errors: HTTP 403/404/429 map to PermissionDeniedError, NotFoundError
and RateLimitError; raise_for_response clips oversized error bodies.
- ble decode: scans and streams cloud-decrypted rows, flushing every 25
packets or 2s and remembering each distinct advertisement's answer.
- CLI cleanup: one _org_or_fail() helper for building the Organization,
and the decode table printer reuses _StreamingTablePrinter.
tests/fake_packets.py builds correctly encrypted CTR and EAX
advertisements for the new tests.
Previously, `ble validate` stopped at the first step that failed and reported almost every key problem as "Unable to decrypt packet with given device key". Nothing could tell you which part of a key setup was wrong. `ble validate [--key <key>] [--device-id <id>] [--firmware PATH]` compares up to four sources of evidence and names the mismatch, such as a key registered UNIX_TIME but flashed DEVICE_UPTIME, a 32-byte key in a build that reads only 16 bytes, or a key registered to another device. - --key decodes BLE packets locally, trying both key sizes, both counter modes, a year of clock skew and every AES-EAX period. It reports the device's clock, including one that was never set. - --device-id checks the registered key type, and sends the packets heard to the cloud decode endpoint, which says whose they are. This works with no gateway nearby, and the cloud stores what it decodes. With --key, only this key's packets are sent, so an undecoded packet means the registered key is not this --key. A token without the read-packets scope falls back to whether the backend decoded a packet from the device in the last 10 minutes. - --firmware reads the key size and counter source from the flashed image (hex, ihex, bin, elf) or a build's config (.config, sdkconfig, autoconf.h), and looks for the key's bytes in the image. Pass --key, --device-id, or both. Existing `ble validate --key K --device-id D` invocations still work. --device-id alone needs no key: the cloud picks the device out of everything heard. --key alone needs no credentials, and covers what `ble check-time` did. This change removes `ble detect` and `ble check-time` in favor of `ble validate`. Both removed tools print a usage error and point to `ble validate`. Supporting changes: - validate.py holds the diagnosis, decoupled from the CLI like detect.py. - org.decode_packets() is Organization.decode_packets() for a caller that already resolved the environment, so validate does not spend two requests constructing an Organization. - Device parses encryption, counter_source, period_exponent and the most recent packet time. - crypto: ctr_eid() is ported from hubble-device-sdk and pinned to its tools/ble-adv.py vectors. Also adds decrypt_at() and eax_eid_index(). decrypt() parses a packet once per sweep, not once per counter. tests/fake_packets.py now derives its CTR EIDs from it. - ble.scan() takes an `until` callback to stop early. Signed-off-by: Micah Morefield <micah@hubble.com> Co-authored-by: Paul Buckley <paul@hubble.com>
d3cab2f to
82450a9
Compare
|
Combined with improvements in #58 (comment) -- Added |
Previously, nothing could tell you whether a device's key setup was right, or which part of it was wrong, if it was.
hubblenetwork check-key --device-id <id> --key <key> [--firmware PATH]compares three sources of evidence to find key mismatches. It attempts to decode BLE packets locally with the device's key. It confirms key type and succesfully decoded packets on the backend with the device id. Optionally, the tool can check a firmware file (hex, out, elf) or a built config file (.config, sdkconfig, autoconf.h) to see if the key size and encryption types are selected correctly.Supporting changes:
untilcallback to stop early.