Several months ago I mentioned much of Global B is neither encrypted nor signed. This is ‘mostly’ true, especially on CAN5, but its not as bad as you think. Since our goal is actually being able to repair the vehicle we own, being able to actually place a module on the vehicle that can cooperate is a pretty important hurdle to overcome. The quick TLDR is OEMs have made this insanely difficult… While they disguise these mechanisms as security in reality they just hurt aftermarket repair and basically make any vehicle unrepairable the second they stop making parts. Its many times hard to get new parts on a vehicle that just rolled off the line. Anyway no matter. I can break anything. Let’s talk about SecOc. I hope you like math.
CAN Security (SecOc)
The Lyriq, CT5, and all of GM’s global B vehicles use a large number of CAN busses and a little bit of Ethernet. All the CAN busses, usually (but not always) meet at the Central Gateway module. The gateway will selectively forward traffic from one bus to another (i.e. CAN5 to CAN2). Sometimes it combines multiple frames into one. Sometimes it checks for a MAC sometimes it doesn’t. It depends. But, pretty much all critical vehicle controls will be over CAN or CAN-FD. The exact protocol varies but they are generally one of…
- 500kbit Classic 11/29bit CAN (CAN5 is a notable example)
- 500kbit / 2Mbit FD
- 500kbit / 5Mbit FD
Some frames on CAN will have an MAC. For example a common frame across all frames is the central gateway status and power frame, 0x370. You will notice some data changing across every frame.
| Power mode | Frame | bit37:3 |
|---|---|---|
| Off | 370#E8EE8A693810 |
1 |
| Accessory | 370#68A66F495810 |
2 |
| Run | 370#7C8CB0697810 |
3 |
| Crank | 370#69F659499810 |
4 |
| Propulsion | 370#6BFE8B49B810 |
5 |
These frames contain a MAC (message authentication code) used to authenticate the message/payload. These are SecOc authenticated messages, but they are not encrypted. Anyone is free to parse the messages, and in fact many modules on the bus will process the message even if the auth fails.
SecOC Algoirithim
Each participating module will share a key which is used to both generate the MAC and check its validity. The math (and actual key) is shown below.
input 01 00000370 000000005FEEE249 3810 ^id ^can_id ^freshness ^payload key 0814586a72522fd9b57685de676eb246 CMAC e8ee8a78 b7ba268d 25473fb3 5bf2067c trunc 27 E8EE8A | 011 wire E8EE8A | 011
The exact padding and payload location may vary, and freshness is many times combined with a secondary companion ID.
SecOC Provisioning
SecOC keys are provisioned in the car against the OEM’s servers. It is possible they may be re provisioned several times. When a vehicle is re provisioned (such as a replacement module) then all modules sharing the key must be updated. These keys get provisioned over UDS. The actual provisioned key seen on the wire is encrypted against a key placed in the device at the factory. We will call this the factory key. To initiate the provision you must also be in possession of 3 things. 1. The key used to unlock the ECU ($27), which is unique for every module. 2. The ID of the HSM or other identifier source, which is generally provisioned at factory. 3. A master key, which is provisioned at factory or assembly line.
$27 Access
The first step of provisioning is actually being in the correct state which will almost always require $27 security access over UDS. This will allow you to initiate the UDS diagnostic routine to then provision the actual key.
$27 01 requestSeed, level 1 -> 67 01 <31-byte seed> seed = ecuid(16B) ‖ nonce(15B) $27 02 <12-byte key> sendKey, level 1 -> 67 02 accepted -> 7F 27 35 NRC 0x35, invalidKey
K1 = CMAC-AES128( root_key, subfn(1B) ‖ ecuid(16B) ‖ nonce(15B) ) key = CMAC-AES128( K1, 0xFF ×16 )[0:12]
Fully worked example
Inputs root_key 2b7e151628aed2a6abf7158809cf4f3c AES-128 test key subfn 01 gmECUID 0102030405060708090a0b0c0d0e0f10 illustrative nonce 112233445566778899aabbccddeeff illustrative, module-generated Step 1: the seed, i.e. what $27 01 returns on the wire (31B) seed = gmECUID ‖ nonce = 0102030405060708090a0b0c0d0e0f10112233445566778899aabbccddeeff Step 2: first CMAC input (32B) msg1 = subfn ‖ seed = 010102030405060708090a0b0c0d0e0f10112233445566778899aabbccddeeff K1 = CMAC_root_key(msg1) = 344944c7c7a8e103f1c9703cca19bf52 Step 3: second CMAC, over a block of 0xFF msg2 = ffffffffffffffffffffffffffffffff full_tag = CMAC_K1(msg2) = ad3ccd21e367a121640b2a233ec7482a Step 4: truncate to 12 bytes; this is the $27 02 payload key_response = full_tag[0:12] = ad3ccd21e367a121640b2a23
Wire trace with those numbers:
$27 01
-> 67 01 0102030405060708090a0b0c0d0e0f10112233445566778899aabbccddeeff
$27 02 ad3ccd21e367a121640b2a23
-> 67 02 accepted
SecOc Provision
Once 27 access is achieved you can begin the UDS Diagnostic Routine to provision the new key. What that routine is (i.e. its RID) and what it looks like will vary module to module, but the general algo is the same.
Inputs auth_key 00112233445566778899AABBCCDDEEFF authorizing key, typically slot 1,0, or some efuse state new_key FEDCBA9876543210FEDCBA9876543210 key being installed into the target UID 000102030405060708090A0B0C0D0E0F0102 120-bit HSM unique ID ID 5 target slot, 4 bits AuthID 1 authorizing slot, 4 bits counter 0x0000001 28-bit anti-replay counter; flags 0000
Step 1: derive K1, K2 from auth_key (SHE KDF = Matyas-Meyer-Oseas over AES-128) KDF(key, const) = AES_Encrypt(key, const) XOR const K1 = KDF(auth_key, KEY_UPDATE_ENC_C) K2 = KDF(auth_key, KEY_UPDATE_MAC_C) KEY_UPDATE_ENC_C = 0x01015348450080000000000000000B0 KEY_UPDATE_MAC_C = 0x01025348450080000000000000000B0 Step 2: build M1 (16 bytes) M1 = UID(120b) ‖ ID(4b) ‖ AuthID(4b) Step 3: build M2 (32 bytes) plaintext = counter(28b) ‖ flags(4b) ‖ new_key(128b) 20 bytes M2 = AES-128-CBC_Encrypt(K1, IV=0x0, plaintext) Step 4: build M3 (16 bytes) M3 = CMAC-AES-128(K2, M1 ‖ M2) Step 5 M1 | M2 | M3 = 64 bytes total
This output is then sent to the target ECU, the math is reversed to derive new_key and the resulting value is loaded into the HSM.
31 01 02 00 <slot:1B> <M1:16B> <M2:32B> <M3:16B> ^^ ^^ ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | the 65-byte option record (param_4 == 0x41) | | RID, big-endian | subfunction 01 = startRoutine routineControl response 71 01 02 00 <status>
The above system would need to be replicated for any aftermarket repair. The OEMs are the only ones who actually have the secret keys on their servers, and they dont have a path to provision aftermarket ones. Given they would prefer selling used vehicles or pushing people toward dealerships for repair, this is a massive conflict of interest.
Ethernet Security
Ethernet is a singular network subdivided using vlans.
The entire vehicle’s CAN network is sent over VLAN 502 as a UDP multicast message, so the easiest way to monitor an entire global b vehicle is interestingly. A pcap. Some switches will block vlan traffic but much of its open. In ours this was sourced from 172.16.50.207->239.192.0.9 (multicast) on port 59200.
element header (7B):
type 1B 0x02 = CAN/CAN-FD frame
body_length 2B big-endian
element_id 4B little-endian the CAN ID
type-0x02 body (16 + data_len bytes):
extended 1B 0 = standard 11-bit, 1 = extended 29-bit
dlc 1B CAN DLC code (0-8 classic, 9-15 -> 12..64 FD)
actual_len 1B data length in bytes, must match DLEN[dlc]
metadata[5]:
[0] controller (mirror, must equal [2])
[1] FD marker byte (bit 0x30 set -> CAN-FD record)
[2] controller <- authoritative channel (fca0..fca9)
[3] flags (0x01, 0x09 dominant; 0x04/0x0A/0x0B rare)
[4] unused/reserved
rolling[4] 4B (timestamp-like counter, endian TBD)
data[actual_len] the CAN payload bytes
trailer[4] 4B = rolling, byte-reversed (integrity check)
On at least the vehicles I looked at, there was no authentication of any form on the in vehicle Ethernet. At least, not at the layer 2 or layer 3 level i.e. IpSec or MacSec. Some niche scenarios do use TLS, but so does watching YouTube on the car, so that’s not really note worthy.
Breaking SecOc
Mathematically it’s not possible. But the world is messy. 0814586a72522fd9b57685de676eb246 is a real secoc key. From a real car… Well its totaled, but it was real at one point. Taken with zero days which will just be patched at some point…. I will publish them, But not today. Regardless I think its possible that all SecOc keys can be dumped. From any module. From any truck or car. No exploits required. You just need to think fast. Really fast.