timdorr / tesla-api

🚘 A Ruby gem and unofficial documentation of Tesla's JSON API for the Model S, 3, X, and Y.
https://tesla-api.timdorr.com/
MIT License
2k stars 534 forks source link

Tesla Signaling/Hermes Protocol to send vehicle commands #768

Closed lotharbach closed 11 months ago

lotharbach commented 11 months ago

Related to #763 I began looking into how the Tesla App now sends signed commands to the vehicle. I would like to share what I have found by intercepting the App network traffic, which is very easy to do with a rooted phone as there is no certificate pinning, and hope for more research to happen. I hope we will someday be able to properly document what I would call the "Tesla Signaling/Hermes Protocol" as part of this project.

First of all, the owner API is still heavily used by the app. There is no reason to suspect the owner API in its entirety will go away anytime soon, as I have read so many times. It will "just" loose all "api/1/vehicle/../command/.." REST endpoints, as vehicles require end-to-end signed commands which is a thousand times more secure and to the benefit of all vehicle owners! The wake_up and vehicle_data endpoint is used by the app every few seconds, and pure data loggers will continue to work just fine!

The Tesla App can not send commands without also being registered as a phone key. For the phone key function it generates a private key on the phone and likely transfer that public key to the vehicle via Bluetooth. Details about generating and transfering such a key is also found in teslas vehicle-sdk. The SDK also contains all there we kneed to know about generating signed commands and can send them via the Fleet API or via Bluetooth. Since we likely won't have access to the Fleet API as vehicle owners, I was interested how the App continues to function.

The App performs a POST on HERMES_AUTHORIZATION endpoint, https://owner-api.teslamotors.com/api/1/users/jwt/hermes

Payload {"uuid": "<some uuid>"}
Response {  "token": "<jwt to talk to hermes>" }

There is also a second POST on HERMES_VEHICLE_AUTHORIZATION endpoint, https://owner-api.teslamotors.com/api/1/vehicles/:vehicle-id/jwt/hermes

Payload {"uuid": "<some uuid>"}
Response {  "token": "<jwt found in some protobuf messages>" }

With the first token, a WebSocket connection is opened on HERMES_URI wss://signaling.vn.teslamotors.com/v1/mobile with Header "X-Jwt: <jwt to talk to hermes>

And they now exchange some sort of protobuf based binary packets. A command in the App very clearly generates a new exchange on that WebSocket channel and nothing else on the network. Decoding the protobuf shows strings like vehicle_device.<my VIN>.cmds and what I guess is a wrapped message in the same signed command protobuf layout that the vehicle-sdk is generating to send via BLE or to the Fleet-API's signed_command endpoint.

This is the end of my knowledge so far. More revesing of the protobuf message description is required to generated what I guess is first some "handshake" and then the command messsages. I unfortunately can't/won't share my full binary capture as it contains private information and credentials.

This is how one can talk to hermes on the command line:

websocat -t --insecure -H="X-Jwt: $(cat hermestoken)" --ws-c-uri=wss://signaling.vn.teslamotors.com:443/v1/mobile ws-c:log:ssl:tcp:signaling.vn.teslamotors.com:443 -
[WARN  websocat::ssl_peer] Connected to TLS without proper verification of certificate. Use --tls-domain option.
WRITE 596 "GET /v1/mobile HTTP/1.1\r\nX-Jwt: redacted\r\nHost: signaling.vn.teslamotors.com\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: 9rN06emPdZj1rpY+/2rNCA==\r\n\r\n"
READ 534 "HTTP/1.1 101 Switching Protocols\r\nDate: Sun, 26 Nov 2023 13:18:00 GMT\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Accept: ihuyLme774eaL2dZutRzHiTIz80=\r\nX-Server-Canonical-Url: wss://hermes-eu-west-prd.vn.tesla.services:443/v1/mobile\r\nStrict-Transport-Security: max-age=15724800; includeSubDomains\r\nContent-Security-Policy: default-src \'none\'\r\nCache-Control: no-cache, no-store, private, s-max-age=0\r\nStrict-Transport-Security: max-age=31536000; includeSubDomains\r\nX-Content-Type-Options: nosniff\r\nX-Frame-Options: DENY\r\n\r\n"
READ 563 "\x82~\x02/\n\xac\x04\n$f673c134-46d8-4e17-af06-f4b01727cb6f\x12\x08internal\x1a\x0c\x08\x88\x8b\x8d\xab\x06\x10\x84\x84\x8f\x9f\x01\"\x0cHermesServer(\xa0\x08J\x0b\x08\x80\x92\xb8\xc3\x98\xfe\xff\xff\xff\x01R\xcb\x03{\"expiration\":1701008280,\"connection_timeout\":180000,\"ping_frequency\":60000,\"autopark\":{\"heartbeat_frequency\":100,\"autopark_pause_timeout\":2000,\"autopark_stop_timeout\":10000},\"ice_servers\":[{\"urls\":[\"stun:46.137.45.143:3478\",\"stun:63.35.119.211:3478\"]},{\"urls\":[\"turn:54.78.132.75:3478\",\"turn:54.228.219.44:3478\",\"turn:turn6.euw1.vn.cloud.tesla.com:3478\"],\"username\":\"redacted:redacted\",\"credential\":\"redacted=\"}]}Z\x00"
 � $f673c134-46d8-4e17-af06-f4b01727cb6internal
                                              ��������"
                                                       HermesServer(J
                                                                    �������R�{"expiration":1701008280,"connection_timeout":180000,"ping_frequency":60000,"autopark":{"heartbeat_frequency":100,"autopark_pause_timeout":2000,"autopark_stop_timeout":10000},"ice_servers":[{"urls":["stun:46.137.45.143:3478","stun:63.35.119.211:3478"]},{"urls":["turn:54.78.132.75:3478","turn:54.228.219.44:3478","turn:turn6.euw1.vn.cloud.tesla.com:3478"],"username":"1redacted","credential":"redacted"}]}Z
READ 563 "\x82~\x02/\n\xac\x04\n$13043831-42b5-4b30-a11f-2fb6217d7db1\x12\x08internal\x1a\x0c\x08\x8d\x8b\x8d\xab\x06\x10\xbd\xc7\xb2\x9f\x01\"\x0cHermesServer(\xa0\x08J\x0b\x08\x80\x92\xb8\xc3\x98\xfe\xff\xff\xff\x01R\xcb\x03{\"expiration\":1701008285,\"connection_timeout\":180000,\"ping_frequency\":60000,\"autopark\":{\"heartbeat_frequency\":100,\"autopark_pause_timeout\":2000,\"autopark_stop_timeout\":10000},\"ice_servers\":[{\"urls\":[\"stun:54.228.219.44:3478\",\"stun:46.137.45.143:3478\"]},{\"urls\":[\"turn:54.78.132.75:3478\",\"turn:63.35.119.211:3478\",\"turn:turn6.euw1.vn.cloud.tesla.com:3478\"],\"username\":\"redacted\",\"credential\":\"redacted\"}]}Z\x00"
 � $13043831-42b5-4b30-a11f-2fb6217d7dbinternal
                                              �����Dz�"
                                                      HermesServer(J
                                                                   �������R�{"expiration":1701008285,"connection_timeout":180000,"ping_frequency":60000,"autopark":{"heartbeat_frequency":100,"autopark_pause_timeout":2000,"autopark_stop_timeout":10000},"ice_servers":[{"urls":["stun:54.228.219.44:3478","stun:46.137.45.143:3478"]},{"urls":["turn:54.78.132.75:3478","turn:63.35.119.211:3478","turn:turn6.euw1.vn.cloud.tesla.com:3478"],"username":"1redacted","credential":"1redacted"}]}Z
READ 561 "\x82~\x02-\n\xaa\x04\n$7c3a3ed4-33d1-4a64-a4db-9d788b2ce3fa\x12\x08internal\x1a\x0c\x08\x92\x8b\x8d\xab\x06\x10\xc3\xc9\xc8\x9f\x01\"\x0cHermesServer(\xa0\x08J\x0b\x08\x80\x92\xb8\xc3\x98\xfe\xff\xff\xff\x01R\xc9\x03{\"expiration\":1701008290,\"connection_timeout\":180000,\"ping_frequency\":60000,\"autopark\":{\"heartbeat_frequency\":100,\"autopark_pause_timeout\":2000,\"autopark_stop_timeout\":10000},\"ice_servers\":[{\"urls\":[\"stun:54.78.132.75:3478\",\"stun:63.35.119.211:3478\"]},{\"urls\":[\"turn:54.78.132.75:3478\",\"turn:52.18.36.139:3478\",\"turn:turn6.euw1.vn.cloud.tesla.com:3478\"],\"username\":\"1redacted\",\"credential\":\"1redacted\"}]}Z\x00"
 � $7c3a3ed4-33d1-4a64-a4db-9d788b2ce3finternal
                                              ������ȟ"
                                                      HermesServer(J
                                                                   �������R�{"expiration":1701008290,"connection_timeout":180000,"ping_frequency":60000,"autopark":{"heartbeat_frequency":100,"autopark_pause_timeout":2000,"autopark_stop_timeout":10000},"ice_servers":[{"urls":["stun:54.78.132.75:3478","stun:63.35.119.211:3478"]},{"urls":["turn:54.78.132.75:3478","turn:52.18.36.139:3478","turn:turn6.euw1.vn.cloud.tesla.com:3478"],"username":"11redacted","credential":"1redacted"}]}Z
^C

Without a valid token there is a "authorization denied" message.