Skip to content

fix(ble): guard empty publicKey in session-info authentication - #138

Merged
Bre77 merged 2 commits into
mainfrom
fm/craig-ble-whitelist-crash
Aug 27, 2026
Merged

fix(ble): guard empty publicKey in session-info authentication#138
Bre77 merged 2 commits into
mainfrom
fm/craig-ble-whitelist-crash

Conversation

@Bre77

@Bre77 Bre77 commented Aug 27, 2026

Copy link
Copy Markdown
Member

Intent

Fix a BLE pairing crash: when the vehicle replies to the VEHICLE_SECURITY handshake with SESSION_INFO_STATUS_KEY_NOT_ON_WHITELIST, _authenticate_session_info in tesla_fleet_api/tesla/vehicle/commands.py previously called session.keys_for(info.publicKey) before checking info.status. A key-not-on-whitelist reply carries an empty publicKey (no session exists yet for an unpaired key), so key derivation raised an uncaught ValueError: data must not be an empty byte string instead of the intended NotOnWhitelistFault. This made the Home Assistant approve-key-in-vehicle pairing flow crash instead of prompting the user, blocking all first-time BLE pairing. Fix: added an empty-publicKey guard before key derivation - when publicKey is empty and status is KEY_NOT_ON_WHITELIST, raise NotOnWhitelistFault unauthenticated (no shared key can be derived to verify a tag in this case, matching Tesla's own vehicle-command client behavior); when publicKey is empty with any other status, raise a typed SessionInfoAuthenticationFault instead of letting the raw ValueError escape. The existing tag-verified-before-whitelist-trust behavior is preserved for the case where publicKey is non-empty (an existing test locks that in), so the fix is a narrow reorder plus one guard, not a restructuring of the session machinery. Added two new tests covering the empty-publicKey cases in tests/test_session_info_authentication.py. Version bumped 1.11.0 -> 1.11.1 (patch, bug fix) per repo release convention, with uv lock re-run to keep uv.lock in sync.

What Changed

  • _authenticate_session_info (tesla_fleet_api/tesla/vehicle/commands.py) now checks info.publicKey before calling session.keys_for(...): an empty key with SESSION_INFO_STATUS_KEY_NOT_ON_WHITELIST raises NotOnWhitelistFault unauthenticated (no shared key can be derived to verify a tag in this case), while an empty key with any other status raises SessionInfoAuthenticationFault instead of letting a raw ValueError escape from key derivation. The existing tag-verified-before-whitelist-trust behavior for non-empty publicKey replies is unchanged.
  • SessionInfoAuthenticationFault's docstring (tesla_fleet_api/exceptions.py) documents the new empty-publicKey/non-whitelist-status case.
  • Added tests/test_session_info_authentication.py coverage for the two new empty-publicKey cases.
  • Bumped version 1.11.0 → 1.11.1 (pyproject.toml, tesla_fleet_api/__init__.py) with uv.lock regenerated to match.

Risk Assessment

✅ Low: Narrow, well-contained fix: an empty-publicKey guard is added before key derivation in _authenticate_session_info, raising NotOnWhitelistFault for the documented KEY_NOT_ON_WHITELIST case and a typed SessionInfoAuthenticationFault otherwise, exactly matching the stated intent; the existing tag-verified-before-whitelist-trust path for non-empty publicKey is untouched, two new tests exercise real behavior (exception type and session.ready state) rather than source text, and the version bump/uv.lock are consistent.

Testing

Ran the targeted session-info authentication test suite on the target commit (25/25 pass) and separately reproduced the pre-fix failure by running the same new tests against the base commit in a scratch worktree, where they fail with the exact ValueError described in the intent — this is a genuine regression test proving the crash is fixed, not just new tests that happen to pass. Version/lockfile bump also verified consistent. No issues found; scratch worktree used for the base-commit repro was removed afterward and the working tree is clean.

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • uv run pytest tests/test_session_info_authentication.py -v on target commit 50d51c3 — all 25 tests pass, including the two new empty-publicKey cases
  • Reproduced the pre-fix crash: checked out base commit f1822c8 into a scratch worktree, copied the target's new test file in, ran uv run pytest tests/test_session_info_authentication.py -k empty_public_key -v — both new tests fail with the exact reported ValueError: data must not be an empty byte string raised from EllipticCurvePublicKey.from_encoded_point, confirming the tests genuinely exercise the reported bug and the fix resolves it
  • Confirmed version bump 1.11.0 -> 1.11.1 is reflected consistently in pyproject.toml, tesla_fleet_api/__init__.py, and uv.lock per repo release convention
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Bre77 added 2 commits August 27, 2026 20:23
…icKey

A KEY_NOT_ON_WHITELIST session_info reply carries no publicKey, since no
session exists yet for an unpaired key. _authenticate_session_info derived
shared keys from that key before checking status, raising an uncaught
ValueError from cryptography instead of NotOnWhitelistFault - breaking the
approve-key-in-vehicle pairing flow for every first-time BLE pairing.

Check for an empty publicKey before key derivation: a whitelist-rejection
status is accepted unauthenticated (no shared key can be derived to verify
it), and any other status with an empty key raises a typed
SessionInfoAuthenticationFault instead of the raw ValueError.
@Bre77 Bre77 added the fm Opened by a Firstmate crewmate label Aug 27, 2026
@Bre77
Bre77 merged commit e49f1e0 into main Aug 27, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fm Opened by a Firstmate crewmate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant