Tesla BLE vehicle-command integration (Phases 0-4) - #58
Conversation
5e0916c to
cd5bfab
Compare
wpmed92
left a comment
There was a problem hiding this comment.
I like the PR, but left some comments, let's discuss them, and apply changes where needed.
|
@dkneeland What I forgot in the review comments, so adding separately, is that now Tesla - if paired, will opccupy another BLE slot, I see you bumped to |
Reviewer (wpmed92) comments plus follow-ups, applied on top of the already-merged Tesla BLE work: - BLE slot/bond/activity config: BT_NIMBLE_MAX_CONNECTIONS 4->6, BT_NIMBLE_MAX_BONDS 3->8, BT_NIMBLE_MAX_CCCDS 8->16, and BT_CTRL_BLE_MAX_ACT 6->8. Corrected the sdkconfig.defaults note that claimed ESP32-S3 "does not expose" the controller activity limit (the S3 controller Kconfig sources the C3 one, so it IS exposed and is the real ceiling). Cost: ~2.9 KB RAM, ~2.5 KB of the 24 KB NVS partition at saturation. - Proto-sync guarantee: new tools/check_proto_sync.sh + CI step that diffs the vendored protos against the pinned teslamotors/vehicle-command commit and regenerates the nanopb bindings asserting no drift. Verified locally: protos match upstream at f97fa1e..., generated bindings have zero content drift. - Comment-verbosity pass across the whole stack: stripped "Phase N"/"plan §N"/internal review-reference tokens from sources, build files, READMEs, CI step names and test suite labels; compressed over-long comments; kept the security and protocol-quirk notes. - VIN privacy: removed the developer's real VIN and its derived advertisement name everywhere; replaced with a synthetic, check-digit-valid VIN (5YJ3E1EB8TF024681 -> legacy advert name S1481f4f405d98dfeC). The observer auto-provision now matches the placeholder name only; app-driven enrollment covers any VIN (Kconfig-izing the target is the follow-up). - On-device verification (COM3): boots clean at the new limits; live on-car handshake + decrypted GET_STATUS still work (sleeping-car retry pattern unchanged). Functional changes are limited to the config bumps and the inert observer target placeholder; the rest is comments/docs/test-label cleanup.
Implements the 2026-08-21 PR softwiredtech#58 audit recommendations: - Remove the NimBLE observer entirely (scan task, discovery handler, advert-name matcher + its test, fake-beacon project). The phone scans for the car instead; the firmware never scans. Drops the hardcoded target VIN (the PR previously only staged one specific car) and the OBSERVER role requirement (#error guard, Kconfig select, sdkconfig default). - New app-channel opcode 0x04 TESLA_CMD_PROVISION (25 bytes: opcode + 17-byte VIN + BLE address type + 6 MAC bytes in ble_addr_t.val order) stages the car via tesla_pairing_configure(); enrollment still starts only on 0x01. - Dedup identical status frames in ble_appchan_report_status(): the pairing task's staged/never-enrolled loops were notifying the same frame at 5 Hz / 1 Hz to a connected phone. - Drop the 4 unused vendored protos (car_server/vehicle/managed_charging/ common - Infotainment-only, imported by nothing we generate); update protos/README, gen_proto.py and check_proto_sync.sh comments. - Delete dead tesla_authenticated_command_key() (+ its openssl vector). - Hoist duplicated RX_FRAME_MAX -> TESLA_RX_FRAME_MAX (adapter.h) and the two identical hw_rng wrappers -> tesla_rand.c. - Consolidate the four test runners' copy-pasted mbedTLS download/build block into tools/test/tesla_host_env.sh; drop the advert-name CI step. Verified: host suites green (crypto/session/enrollment), idf.py build clean with both a stale sdkconfig and a fresh reconfigure (OBSERVER=n); binary size unchanged at 69% OTA free.
…ope-cut rework The fork head carried the original Phase 0-4 commits. The scope-cut branch reimplements that content with review fixes, the app-driven provisioning flow, and the audit remediation (dynamic-bytes binding, link-only keepalive, fast-fail connect). Taking our tree wholesale; original commits preserved as ancestry.
|
Thanks @wpmed92 — all five inline comments plus your NimBLE capacity note are addressed, alongside a scope revision and several vehicle-validation fixes. Pushed to Your review points
Separate issue comment (NimBLE slots): bumped well past 4 — Scope change since you reviewed: the firmware-side observer / advert-name matching, the infotainment protos, and Vehicle-validation fixes riding along: regenerated |
… layers, mbedTLS 3.6 Vendor the Apache-2.0 Tesla vehicle-command .proto schemas (pinned in protos/VERSION at upstream f97fa1e4) and the nanopb 0.4.9.1 runtime, and add the crypto/session layers ported to the mbedTLS 3.6 API used by ESP-IDF 5.4.1: P-256 ECDH, K = SHA1(X)[:16], HMAC-SHA256 session/auth subkeys, AES-128-GCM sign + decrypt (AAD = SHA256(metadata)), metadata TLV sort/build, session-info HMAC auth, request-hash, and the committed nanopb VCSEC bindings plus the protobuf message builders. tools/gen_proto.py regenerates the bindings; tools/check_proto_sync.sh fails on vendored-proto or binding drift. The protocol layer has no BLE dependency (Phase 0 scope).
…-framed RX/TX, link keepalive NimBLE central adapter for the vehicle-command GATT service: connect with a generation-token that rejects stale/late callbacks, exchange MTU, discover service 00000211 / write 0212 / indicate 0213, subscribe notify+indicate, 2-byte BE length-prefixed write framing + RX reassembly, and a link-only keepalive (CCCD read) that resets supervision without touching the VCSEC stack. Idle-disconnect + abort/cleanup reconcile the async transport. The central GAP path is kept strictly separate from the peripheral server's gap_event_handler (per ADR 0001 note 2) and never feeds ble_server's slot table.
…esent-key enrollment Phases 2-4 of the plan. ble_appchan (CADA02xx) is the app-triggered pairing channel (opcodes 0x01 start / 0x02 reset / 0x03 cancel / 0x04 provision VIN+MAC) with status-notify + read of a link_state frame; the firmware never self-arms pairing and ships no BLE observer. tesla_ble_client runs the persistent VCSEC poll (handshake -> GET_STATUS, anti-replay + GCM decrypt, debounced not-connected reporting). tesla_ble_storage persists the keypair/VIN/car address in NVS. tesla_pairing does present-key enrollment (CHARGING_MANAGER, card tap + touchscreen confirm) with handshake-verified persistence. Review fixes folded in: status dedup centralized in ble_appchan_report_status (single full-frame memcmp); status notify documented as targeting only the single active connection.
…anary DASHKIT_TESLA_BLE gate (default y) adds the tesla-protocol component, the main/tesla sources, the app-channel service, and the BLE central role; disabling drops them from the build. sdkconfig.defaults enables the central role and bumps MAX_CONNECTIONS. main.c gains the central #error guard and a boot canary.
… drift check Host-side (no ESP-IDF) unit tests over the crypto/session/protobuf layers, validated against Tesla's protocol.md known-answer vectors: crypto 61/61, session round-trip (handshake, GET_STATUS sign/decrypt, anti-replay, whitelist state machine), and enrollment (keygen + present-key message). A CI 'test' job builds a pinned mbedTLS 3.6.2 and runs the suites plus check_proto_sync.sh.
cde40a5 to
b2fc173
Compare
Summary
Full Tesla BLE (vehicle-command) integration, Phases 0-4 of the implementation plan, developed and on-car validated on the dkneeland fork. This PR brings the whole line into the official repo so others can flash and test the BLE onboarding flow.
What's included
components/tesla-protocol/- vendored Apache-2.0 Tesla protos (pinned inprotos/VERSION) + nanopb runtime + crypto/session layers ported to mbedTLS 3.6, validated against protocol.md known-answer vectors (61/61 host tests + session/enrollment suites).0211, write0212, indicate0213), VCSEC handshake, GET_STATUS poll; response anti-replay + GCM decrypt per review.CHARGING_MANAGER, NVS persistence - on-car validated 2026-08-19.ble_appchan.c, CADA02xx) with app-triggered-only pairing. The phone discovers + provisions the car's VIN/MAC via opcode0x04;0x01starts enrollment,0x02factory-resets,0x03cancels. Includes staged-car state and fault reporting. The firmware never self-arms pairing and ships no BLE observer (Phase 1 was cut in the scope audit - the app stages the car instead of the firmware scanning).KeyIdentifier.publicKeySHA1as dynamic bytes (real cars send truncated 4-byte ids); whitelist-result classifier; pairing-window/enrolled report timing aligned with real car state.Validation
check_proto_sync.shfails on vendored-proto or binding drift (nanopb pinned).Notes for reviewers / testers
CONFIG_DASHKIT_TESLA_BLEdefaults to y: every build now includes the Tesla central role and the tesla-protocol component. Harmless with no car provisioned (the client idles; nothing scans or self-stages without app input), but call it out for flashing.link_statebyte) is new. Provisioning is app-driven: the phone scans for the car and sends VIN + BLE address via opcode0x04.GET_STATUS send failedon retries (Phase 6 robustness item); private key stored in plaintext NVS (flagged release-blocker-candidate, hardening deferred); role is CHARGING_MANAGER only (DRIVER opt-in is Phase 5); app-channel commands are authenticated only by BLE link-layer encryption (no app-level secret - acceptable for the car-trim threat model, but not to be treated as strong auth).Update 2026-08-23 - scope cut, review remediation, app-driven onboarding
This PR has been substantially revised since the initial push. The branch was rebuilt as
feat/tesla-ble-scope-cut(now pushed here) containing all original content plus fixes found in vehicle validation and an internal audit. Headline changes vs the description above:Scope cut (firmware gets smaller, not bigger)
tesla_advert_name.*, its test suite, andtools/fake_tesla_beacon/are deleted).car_server/common/managed_charging/vehicle.proto+ bindings) are removed; only the VCSEC/vehicle-command schemas we actually use remain vendored.Fixes from vehicle validation
KeyIdentifier.publicKeySHA1regenerated as dynamic-length bytes: real cars send truncated 4-byte ids insidesignerOfOperation, and a fixed-length binding rejected those whole frames (the tap result silently vanished). Host regression test pins the captured frame.connect()immediately instead of riding out the 20 s timeout.0x06 Connectingkeeps the app informed between staging and the tap window.nanopb==0.4.9.1;check_proto_sync.shfails on vendored-proto or binding drift.Reviewer items (see comment thread): proto pinning/sync OK, generated-bindings policy OK, Kconfig scoping OK, NimBLE capacity bumps OK, storage-header verbosity OK, advert-name question moot after scope cut OK.