A long post, but contains some interesting information, and discussion of UDS hacking/cracking approaches. A few weeks ago, I published my work on the Baolong TPMS in the Tesla Roadster. This resulted in a new TPMS subsystem in OVMS, as well as support for reading and writing tyre ID sets from/to the TPMS ECU in the car. For the Tesla Roadster, this requires the new K-line option board that should be generally available next week. I then moved on to look at the Model S. My car (a 2014 Model S) uses a Baolong ECU, and can’t display the tyre pressures or temperatures. It is a simpler system than the roadster (same manufacturer), with just one ECU, one CAN bus connection, no k-line, no Lin bus, and no external antennas. Also about US$40 on eBay :-) Decoding the message data was pretty trivial. Same format as the Roadster (same manufacturer, after all), just on a different CAN ID. The status messages are different, but the Tesla Model S ECU in developer mode shows a nice decode of those. A few minutes in the OVMS firmware and we can display tyre pressures and temperatures in the OVMS Apps (which strangely, Tesla still can’t do with that Baolong system in the car). A colleague provided some CAN bus dumps of the engineering TPMS tool reading and writing tyre ID sets, and they looked pretty simple. First the read commands: 34.296622 2T11 64F 03 22 f0 90 55 55 55 55 34.310594 2R11 65F 10 13 62 f0 90 11 12 13 34.396020 2T11 64F 30 03 0a 00 00 00 00 00 34.396663 2R11 65F 21 14 21 22 23 24 31 32 34.415594 2R11 65F 22 33 34 41 42 43 44 55 Looks pretty similar to the stuff we’ve seen before with OBDII active polling. ID 64F for the command and 65F for the response from the ECU. An extended OBDII multi-frame read of PID 0xf090. Easy enough, and implemented in the vehicle_teslamodels in less than an hour. So, on to the write commands: 114.506522 2T11 64F 02 10 03 00 00 00 00 00 114.552387 2R11 65F 06 50 03 00 96 17 70 55 120.956904 2T11 64F 02 27 01 00 00 00 00 00 120.957400 2R11 65F 04 67 01 b5 0a 55 55 55 144.106603 2T11 64F 04 27 02 f7 02 00 00 00 144.107136 2R11 65F 02 67 02 55 55 55 55 55 157.657510 2T11 64F 10 13 2e f0 90 11 12 13 157.657932 2R11 65F 30 03 14 55 55 55 55 55 157.756158 2T11 64F 21 14 21 22 23 24 31 32 157.856138 2T11 64F 22 33 34 41 42 43 44 00 158.148199 2R11 65F 03 6e f0 90 55 55 55 55 At first glance, I skipped over the first three commands (assuming they were checking firmware version, or something like that), and looked at the last group. Seems similar to the OBDII extended read we saw to read the IDs. But when I try it on my bench ECU I don’t get the happy ‘65F 03 6e f0 90’ acknowledging the write, but instead a '65F 03 7f 2e 31’ indicating an error. So, I went back and looked at those first three requests. In OBDII speak, these decode as: 64F 02 10 03 00 00 00 00 00 0x02 = 2 data bytes in the request 0x10 = UDS Diagnostic Session Control 0x03 = UDS session type 0x03 65F 06 50 03 00 96 17 70 55 0x06 = 6 data bytes in the response 0x50 = 0x10 + 0x40 = successful response to our 0x10 command 0x03 = UDS session type 0x03 0x00961770 = Session data 64F 02 27 01 00 00 00 00 00 0x02 = 2 data bytes in the request 0x27 = UDS Security Access 0x01 = Type 1 (seed request) 65F 04 67 01 b5 0a 55 55 55 0x04 = 4 data bytes in the response 0x67 = 0x27 + 0x40 = successful response to our 0x27 command 0x01 = Type 1 (seed) 0xb50a = UDS security access seed data 64F 04 27 02 f7 02 00 00 00 0x04 = 4 data bytes in the request 0x27 = UDS Security Access 0x02 = type 2 (login) 0xf702 = UDS security access login data 65F 02 67 02 55 55 55 55 55 0x02 = 2 data bytes in the response 0x67 = 0x27 + 0x40 = successful response to our 0x27 command 0x02 = type 2 (login success) * See here for an explanation: https://en.wikipedia.org/wiki/Unified_Diagnostic_Services <https://en.wikipedia.org/wiki/Unified_Diagnostic_Services> At the point, I had an ‘oh shit’ moment. Why they hell would they use UDS security to protect writing TPMS tyre IDs? Good grief. The way this works is that the diagnostic tool first establishes a diagnostic session with the ECU. It then requests a seed, and the ECU responds with a random number. The tool and ECU both then independently do a secret calculation (based on a secret algorithm and a secret number), the tool provides the result to the ECU, and the ECU verifies it. The seed provides protection against relay attack, and the password or algorithm are never sent over the wire so if done properly this can be strong against eavesdropping. Despite the initial ‘oh shit’, there were two bits of good news. Firstly, the seed was only 2 bytes; that is 65,536 combinations and very susceptible to brute force. Secondly, we had a number of writes logged from the original tool so knew some good seed and login combinations: 843b -> d632 8338 -> d330 8932 -> db32 4738 -> 5730 ff00 -> ff00 That, and in particular the last one, doesn’t seem to show much entropy. The seed->password is just not random enough. I started with some blind guesses: Here are our known samples: 843b -> d632 8338 -> d330 8932 -> db32 4738 -> 5730 ff00 -> ff00 In binary: 843b 1000 0100 0011 1011 d632 1101 0110 0011 0010 8338 1000 0011 0011 1000 d330 1101 0011 0011 0000 8932 1000 1001 0011 0010 db32 1101 1011 0011 0010 4738 0100 0111 0011 1000 5730 0101 0111 0011 0000 ff00 1111 1111 0000 0000 ff00 1111 1111 0000 0000 It seems that the first byte can have 0’s become a 1, but never a 1 become a 0. So let’s assume that is a boolean OR operator. The second byte doesn’t have such good data, but it seems a 1 can become a 0. So let’s assume that it is a boolean AND operator. ----or--- ---and--- 0101 0010 ??11 0?10 In ascii, 0101 0010 is “R”. The second byte is harder, as we don’t have enough data and only know the result for 5 out of the 8 bits. But, if it is a letter, we can guess it is 0111 0?10. In ascii, 0111 0010 is the lower case “r”. So, OR the first byte with “R”, then AND the second byte with “r”. Can it be that simple? Can my first blind guess be correct? A little perl code: #!/usr/bin/perl while (<>) { chop; my $val = hex($_); my $first = $val >> 8; my $second = $val & 0xff; printf "Response for %04x is %02x %02x\n",$val,($first | 0b01010010),($second & 0b01110010) } # Running it… 843b Response for 843b is d6 32 8338 Response for 8338 is d3 30 8932 Response for 8932 is db 32 4738 Response for 4738 is 57 30 ff00 Response for ff00 is ff 00 OK. But that is easy. Let’s try it for some new data… OVMS# can can2 tx standard 64F 02 10 03 00 00 00 00 00 V (11781198) canlog-monitor: 1593874849.354117 2T11 64F 02 10 03 00 00 00 00 00 V (11781248) canlog-monitor: 1593874849.400007 2R11 65F 06 50 03 00 96 17 70 55 OVMS# can can2 tx standard 64F 02 27 01 00 00 00 00 00 V (11786098) canlog-monitor: 1593874854.254102 2T11 64F 02 27 01 00 00 00 00 00 V (11786098) canlog-monitor: 1593874854.254637 2R11 65F 04 67 01 be 01 55 55 55 (My program gives "Response for be01 is fe 00”) OVMS# can can2 tx standard 64F 04 27 02 fe 00 00 00 00 V (11805208) canlog-monitor: 1593874873.364121 2T11 64F 04 27 02 fe 00 00 00 00 V (11805208) canlog-monitor: 1593874873.364639 2R11 65F 02 67 02 55 55 55 55 55 Of course, that is a guess. But after trying a few dozen times I get success 100% of the time. It does make me wonder what is the point of even attempting to add a security layer so trivially easy to crack. I implemented the write in the OVMS firmware for Tesla Model S, and we have: OVMS# tpms read TPMS read as 11121314,21222324,31323334,41424344 V (34357) canlog-monitor: 34.296622 2T11 64F 03 22 f0 90 55 55 55 55 V (34377) canlog-monitor: 34.310594 2R11 65F 10 13 62 f0 90 11 12 13 V (34457) canlog-monitor: 34.396020 2T11 64F 30 03 0a 00 00 00 00 00 V (34457) canlog-monitor: 34.396663 2R11 65F 21 14 21 22 23 24 31 32 V (34477) canlog-monitor: 34.415594 2R11 65F 22 33 34 41 42 43 44 55 OVMS# tpms write models Tyre set 'models' written to vehicle TPMS successfully V (39687) canlog-monitor: 39.627553 2T11 64F 02 10 03 00 00 00 00 00 V (39737) canlog-monitor: 39.673421 2R11 65F 06 50 03 00 96 17 70 55 V (39787) canlog-monitor: 39.726138 2T11 64F 02 27 01 00 00 00 00 00 V (39787) canlog-monitor: 39.726907 2R11 65F 04 67 01 80 3b 55 55 55 V (39887) canlog-monitor: 39.826021 2T11 64F 04 27 02 d2 32 00 00 00 V (39887) canlog-monitor: 39.826744 2R11 65F 02 67 02 55 55 55 55 55 V (39987) canlog-monitor: 39.926014 2T11 64F 10 13 2e f0 90 08 02 ee V (39987) canlog-monitor: 39.926747 2R11 65F 30 03 14 55 55 55 55 55 V (40087) canlog-monitor: 40.026051 2T11 64F 21 37 08 02 e9 63 08 02 V (40187) canlog-monitor: 40.126015 2T11 64F 22 ed fb 08 02 db 3a 00 V (40187) canlog-monitor: 40.126692 2R11 65F 03 7f 2e 78 55 55 55 55 V (40477) canlog-monitor: 40.418525 2R11 65F 03 6e f0 90 55 55 55 55 OVMS# tpms read TPMS read as 0802ee37,0802e963,0802edfb,0802db3a V (62107) canlog-monitor: 62.046624 2T11 64F 03 22 f0 90 55 55 55 55 V (62127) canlog-monitor: 62.060579 2R11 65F 10 13 62 f0 90 08 02 ee V (62207) canlog-monitor: 62.146023 2T11 64F 30 03 0a 00 00 00 00 00 V (62207) canlog-monitor: 62.146658 2R11 65F 21 37 08 02 e9 63 08 02 V (62227) canlog-monitor: 62.165585 2R11 65F 22 ed fb 08 02 db 3a 55 Code committed, and working in my car. Regards, Mark