· 6 years ago · Aug 05, 2019, 04:46 AM
1
2
3https://fccid.io/PIY-HID00-19A5R
4
5# BLE service/characteristics
6
7```
8af0a6ec70001000a84a091559fc6f0de
9 af0a6ec70002000a84a091559fc6f0de: write, indicate // data
10 af0a6ec70003000a84a091559fc6f0de: read // factory
11 af0a6ec70004000a84a091559fc6f0de: write, indicate // session
12
13af0a6ec70001000b84a091559fc6f0de //nxp ota service
14 af0a6ec70002000b84a091559fc6f0de: write, indicate // NXPConfig::otaCmd / NXP_OTAP
15 af0a6ec70003000b84a091559fc6f0de: writeWithoutResponse // NXPConfig::otaData
16```
17
18### OTA Command characteristic (indicate)
19
20`02 00 00 01 00 00 41 11 11 11 01`
21
22Android logs:
23
24```
25NewImageInfoRequest 02 00 00 01 00 00 41 11 11 11 01
26build version 01 00 00
27app Version 41
28hardware Id 11 11 11
29manufacturer 01
30```
31
32
33# Start
34
351. subscribe to 'data' and 'session' characteristics
361. read from 'factory' characteristic
37
38 >01303739394f5331313032303938363239303730303031383902febf6d48ebd6ee1328a875b64def8641554e5bbc99d73c26b2cfc84045528e96005c919764000198967fcd604f291b4aeab61e14449a0ddc4c381b63946b9825df2d86c0548afeef07fe7b6ce4706762b2e5ef4930edb8ea88b977566df13d8f6dd1c743b33a03edf880d5c23603
39
40 NB: for the public key above (0x02febf) the private key is `9f69f7257a3bf97dcd6dee36e740a4e32c353ecb886b8a5b30184380c056d0f8`
41
42 | index | length | name | description | example |
43 |---|---|---|---|---|
44 | 0 | 1 | protocolVersion | | 1 |
45 | 1 | 24 | serial | ascii encoded data, see next table for decoding | 0799OS110209862907000189 |
46 | 25 | 33 | compressedPublicKey | secp256r1/prime256v1 ECDH public key | 02febf6d48ebd6ee1328a875b64def8641554e5bbc99d73c26b2cfc84045528e96 |
47 | 58 | 5 | birthdate | 5 byte unsigned int of unix timestamp | |
48 | 63 | 2 | machineNumber | signed 16bit big endian | 1 |
49 | 65 | 3 | keyID | unsigned 24bit I think used to index into hardcoded array of public keys in app | 9999999 |
50 | 68 | 64 | signature | sha256 based sig (I haven't validated) | |
51 | 132 | 4 | salt | salt used for encryption | d5 c2 36 03 |
52
53 * Serial
54
55 | index | length | name | description | example |
56 |---|---|---|---|---|
57 | 0 | 3 | day | | 079 |
58 | 3 | 1 | year | | 9 |
59 | 4 | 2 | location | | OS |
60 | 6 | 10 | APNumber | | 1102098629 |
61 | 16 | 2 | revision | | 07 |
62 | 18 | 6 | itemNumber | | 000189 |
63
64
651. Write 37 bytes to 'session'
66 * 33 byte secp256r1/prime256v1 ECDH public key (compressed)
67 * 4 byte salt of your choosing
68
691. Starting receiving messages, see format and decryption below
70
71
72### BLE Messages
73
74
75> 7e00000001001555fc7918da6ab8101a1c0fa0d9b19c4c590501ef7b33
76
77| index | length | name | description | example |
78|---|---|---|---|---|
79| 0 | 1 | | Always starts with this byte | 7e |
80| 1 | 4 | packetNumber | | 00 00 00 01 |
81| 5 | 2 | length | | 0015 (21 bytes) |
82| 7 | 1 | header crc8 | crc8 (aka crc8atm) but with a 0xff initial value | `reveng -m "CRC-8" -i ff -c 7E000000010011` |
83| 8 | length | contents | the contents, encrypted | |
84| -1 | 1 | content checksum | same as header checksum, but checked after decrypt | |
85
86### Encryption
87
88
89#### Derive secretKey
90
91Reencrypt sharedsecret with itself 100x:
92
931. ECDH to get a shared secret
941. Create IV with 9 zeros, 'mattel', and a final 0 (16 bytes in total)
951. encrypt shared secret using shared secret and IV
961. IV[7]++
971. repeat until IV[7] is 99
98
99#### Decrypt contents
100
1011. IV is concat of:
102 * 4 byte packetNumber
103 * 4 byte salt from peer
104 * 4 byte salt you chose
105 * 4 bytes of 0
1061. key is first 16 bytes of secretKey
1071. algorithm is aes-128-ctr
108
109To encrypt, reverse order of salts: put your salt before the peer salt
110
111## Parsing descrypted Messages
112
113* Protobuf
114
115```protobuf
116syntax = "proto3";
117package hwid;
118
119message PortalToApp {
120 uint32 timestampMs = 1;
121 Event event = 2;
122 DeviceInfo deviceInfo = 3;
123 CommandResponse commandResponse = 4;
124 bytes accessoryMessage = 5;
125}
126
127enum EventType {
128 UNKNOWN = 0;
129 LOW_BATTERY = 1;
130 CAR_ON = 2;
131 CAR_OFF = 3;
132 CAR_DRIVE_BY = 4;
133 CAR_HIST = 5;
134 ACC_ATTACH = 6;
135 ACC_DETATCH = 7;
136}
137
138message Event {
139 uint32 type = 1;
140 CarInfo carInfo = 2;
141 SpeedMeasurement speedMeasurement = 3;
142 repeated SpeedMeasurement measurementHistory = 4;
143 repeated OfflineRaceSession offlineRaceSessions = 5;
144}
145
146message OfflineRaceSession {
147 uint32 timePlayed = 1;
148 float topSpeed = 2;
149 uint32 scanCount = 3;
150}
151
152message CarInfo {
153 bytes tagUid = 1;
154 bool signatureStatus = 2;
155 bytes carNdef = 3;
156 bytes signature = 4;
157 bytes publicKey = 5;
158}
159
160message SpeedMeasurement {
161 uint32 timestampMs_ = 1;
162 float speed = 2;
163 int32 tIr1In = 3;
164 int32 tIr1Out = 4;
165 int32 tIr2In = 5;
166 int32 tIr2Out = 6;
167}
168
169message DeviceInfo {
170 uint32 firmwareVersion = 1;
171 uint32 hardwareVersion = 2;
172 float batteryLevel = 3;
173 uint32 deviceMode = 4;
174 uint32 bootTimestamp = 5;
175 string serialNumber = 6;
176 uint32 batteryState = 7; // 2: charging, 1: not charging, 3: full, 4: problem
177 uint32 qValue = 8;
178 uint32 iValue = 9;
179 string semanticFirmwareVersion = 10;
180 bool accessoryAttached = 11;
181}
182
183message CommandResponse {
184 int32 failed = 1;
185 string failMessage = 2;
186}
187
188message AppToPortal {
189 uint32 timestampSec = 1;
190 Command command = 2;
191 bytes accessoryMessage = 3;
192}
193
194message Command {
195
196/*
197UnknownCommandType = 0;
198PortalMode = 1;
199RaceMode = 2;
200RequestDeviceInfo = 3;
201TestMode = 4;
202Reset = 5;
203StartOta = 6;
204SetLedcolor = 7;
205ResetLedcontrol = 8;
206*/
207
208 uint32 type = 1;
209 bytes otaSignature = 2;
210 bytes otaPublicKey = 3;
211 bytes rgbColor = 4;
212}
213
214
215message AccessoryToPortal {
216 uint32 timestampMs = 1;
217 DeviceInfo info = 2;
218 bytes accessoryMessage = 3;
219}
220
221message PortalToAccessory {
222 uint32 timestampMs = 1;
223 Event event = 2;
224 bytes accessoryMessage = 3;
225}
226
227```
228
229
230### accessoryMessage
231
232
233| index | length | name | description | example |
234|---|---|---|---|---|
235| 0 | 1 | start-of-message | | 0x8d |
236| 1 | 2 | ? | | 0xffff |
237| 3 | 2 | ? | | 0xfffe |
238| 5 | 2 | Track.CommandType | | 0xbbbc |
239| 7 | 2 | length | | 0x0002 |
240| -2 | 2 | checksum | CRC-16/CCITT-FALSE | |
241
242
243
244| Track.CommandType | value |
245|---|---|---|---|---|
246| TableRequest | 5 |
247| TableResponse | 6 |
248| SetUpstreamBase | 0x10 |
249| RequestBoosterBirthCert | 0x20 |
250| ResponseBoosterBirthCert | 0x21 |
251| RequestTrackBirthCert | 0x22 |
252| ResponseTrackBirthCert | 0x23 |
253| TestLED | 0x30 |
254| FirmwareOtaStart | 0xAAAA |
255| FirmwareOtaManifest | 0xAAAB |
256| FirmwareOtaPackage | 0xAAAC |
257| FirmwareOtaDone | 0xAAAD |
258| Acknowledge | 0xACAC |
259| FinishLineEvent | 0xBBBB |
260| BoosterRpmEvent | 0xBBBC |
261
262
263#### TableResponse
264
265> 8dfffffffe0006001b020041b0b0c60041b0b0c80101ffff000201ffff020200010015116013
266
267Didn't figure out completely, but the first byte is a count of pieces, including the booster, and there are a list of 4 byte ids for the pieces
268
269
270
271| physical name | id | TrackType | Track.Type | SmartTrackID |
272|---|---|---|---|---|
273| booster | 41b0b0c6 | 0 | SmartBooster | |
274| long | 41b0b0c7 | 1 | Track12Inch | |
275| short | 41b0b0c8 | 3 | Track6Inch | |
276| | 41b0b0c9 | 6 | BankedCurve | |
277| gate | 41b0b0ca | 2 | FinishLine | |
278| | 41b0b0cb | 7 | Loop | |
279| jump | 41b0b0cc | 4 | JumpLaunch | |
280| | 41b0b0cd | 5 | JumpLanding | |
281| | | 8 | Unknown | |
282
283
284
285#### ResponseTrackBirthCert
286
287>fffffffe00230058020041b0b0c8005c9099283037383973373032313998967f17b8e7d4d614172abb271d246041d83a3995fe87e7a53f3d7521efaab307461bd290988b2a7a592404c071b5ee99a69e26150004c56a430eb178a6ca5e70bb84
288
289| index | length | name | description | example |
290|---|---|---|---|---|
291| 0 | 2 | ? | | 0xffff |
292| 2 | 2 | ? | | 0xfffe |
293| 4 | 2 | Track.CommandType | | 0x0023 |
294| 6 | 2 | length | | 0x0058 |
295| 8 | 1 | ? | | 0x02 |
296| 9 | 5 | ? | track part id? (seen for track connected messages) | 0041b0b0c8 |
297| 14 | 5 | timestamp | | 0x005c909928 |
298| 19 | 10 | serialnumber | ascii | 0789s70219 |
299| 29 | 3 | keyID | | 9999999 |
300| 32 | 64 | signature? | I assume a signature like the first message | |
301
302
303#### ResponseBoosterBirthCert
304
305>fffffffe00210058020041b0b0c6005c8f4ad63037373973373038393998967f564a16af5f6f4fa95de9ca662f3591bde24602cdae71e95f6eb0d34b718bfd63419fe79b6fa7a052265df6f615a37baaa86465dcc69ff9a4159665ac2f281a7b
306
307
308| index | length | name | description | example |
309|---|---|---|---|---|
310| 0 | 2 | ? | | 0xffff |
311| 2 | 2 | ? | | 0xfffe |
312| 4 | 2 | Track.CommandType | | 0x0021 |
313| 6 | 2 | length | | 0x0058 |
314| 8 | 1 | ? | | 0x02 |
315| 9 | 5 | ? | booster part id? (seen for track connected messages) | 0041b0b0c6 |
316| 14 | 5 | timestamp | | 0x005c8f4ad6 |
317| 19 | 10 | serialnumber | ascii | 0779s70899 |
318| 29 | 3 | keyID | | 9999999 |
319| 32 | 64 | signature? | I assume a signature like the first message | |