blob: 98d2ce32bf2c17c45038f592180125d00227518d [file] [log] [blame]
Will Drewry92973c72017-04-02 15:04:32 -05001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * Commandline tool for interfacing with the Boot Storage app.
17 */
18
19#include <ctype.h>
20#include <inttypes.h>
21#include <stdio.h>
22#include <stdint.h>
23#include <unistd.h>
24
25#include <string>
26#include <vector>
27
28#include <cutils/properties.h>
29#include <ese/ese.h>
30ESE_INCLUDE_HW(ESE_HW_NXP_PN80T_NQ_NCI);
31
32#include "include/ese/app/boot.h"
33
34void usage(const char *prog) {
35 fprintf(stderr,
36 "Usage:\n"
37 "%s <cmd> <args>\n"
38 " state get\n"
39 " production set {true,false}\n"
40 " rollback set <іndex> <value>\n"
41 " get <іndex>\n"
42 " lock get {carrier,device,boot,owner}\n"
43 " set carrier 0 <unlockToken>\n"
44 " <nonzero byte> {IMEI,MEID}\n"
45 " device <byte>\n"
46 " boot <byte>\n"
47 " owner 0\n"
48 " owner <non-zero byte> <keyValue>\n"
Will Drewry80558582017-04-18 13:44:43 -050049 " reset\n"
Will Drewry92973c72017-04-02 15:04:32 -050050 " verify-key test <blob>\n"
51 " verify-key auto\n"
52 "\n"
53 "Note, any non-zero byte value is considered 'locked'.\n"
54 "\n\n", prog);
55}
56
57#define handle_error(ese, result) ;
58#if 0 // TODO
59void handle_error(struct EseInterface *ese, EseAppResult result) {
Will Drewry2c8c5d52017-04-17 10:12:03 -050060 show how to handle different errors, like cooldown before use or after.
Will Drewry92973c72017-04-02 15:04:32 -050061}
62#endif
63
64static void print_hexdump(const uint8_t* data, int start, int stop) {
65 for (int i = start; i < stop; ++i) {
66 if (i % 20 == start - 1) {
67 printf("\n");
68 }
69 printf("%.2x ", data[i]);
70 }
71 printf("\n");
72}
73
74static uint16_t hexify(const std::string& input, std::vector<uint8_t> *output) {
75 for (auto it = input.cbegin(); it != input.cend(); ++it) {
76 std::string hex;
77 hex.push_back(*it++);
78 hex.push_back(*it);
79 output->push_back(static_cast<uint8_t>(std::stoi(hex, nullptr, 16)));
80 }
81 return static_cast<uint16_t>(output->size() & 0xffff);
82}
83
84
85bool get_property_helper(const char *key, char *value) {
86 if (property_get(key, value, NULL) == 0) {
87 fprintf(stderr, "Property '%s' is empty!\n", key);
88 return false;
89 }
90 return true;
91}
92
93// Serializes the data to a string which is hashed by the applet.
94bool collect_device_data(const std::string &modem_id, std::string *device_data) {
95 static const char *kDeviceKeys[] = {
96 "ro.product.brand",
97 "ro.product.device",
98 "ro.build.product",
99 "ro.serialno",
100 "",
101 "ro.product.manufacturer",
102 "ro.product.model",
103 NULL,
104 };
Will Drewrye0925682017-04-18 10:35:21 -0500105 bool collect_from_env = getenv("COLLECT_FROM_ENV") != NULL;
Will Drewry92973c72017-04-02 15:04:32 -0500106 uint8_t len = 0;
107 const char **key = &kDeviceKeys[0];
108 do {
109 if (strlen(*key) == 0) {
110 len = static_cast<uint8_t>(modem_id.length());
111 device_data->push_back(len);
112 device_data->append(modem_id);
113 } else {
114 char value[PROPERTY_VALUE_MAX];
Will Drewrye0925682017-04-18 10:35:21 -0500115 char *value_ptr = &value[0];
116 if (collect_from_env) {
117 // Use the last dotted value.
118 value_ptr = getenv(strrchr(*key, '.') + 1);
119 if (value_ptr == NULL) {
120 fprintf(stderr, "fake property '%s' not in env\n",
121 strrchr(*key, '.') + 1);
122 return false;
123 }
124 } else if (!get_property_helper(*key, value_ptr)) {
Will Drewry92973c72017-04-02 15:04:32 -0500125 return false;
126 }
Will Drewrye0925682017-04-18 10:35:21 -0500127 len = static_cast<uint8_t>(strlen(value_ptr));
Will Drewry92973c72017-04-02 15:04:32 -0500128 device_data->push_back(len);
Will Drewrye0925682017-04-18 10:35:21 -0500129 device_data->append(value_ptr);
Will Drewry92973c72017-04-02 15:04:32 -0500130 }
131 if (*++key == NULL) {
132 break;
133 }
134 } while (*key != NULL);
135 return true;
136}
137
138int handle_production(struct EseBootSession *session, std::vector<std::string> &args) {
139 EseAppResult res;
140 if (args[1] != "set") {
141 fprintf(stderr, "production: unknown command '%s'\n", args[2].c_str());
142 return -1;
143 }
144 if (args.size() < 3) {
145 fprintf(stderr, "production: not enough arguments\n");
146 return -1;
147 }
148 bool prod = false;
149 if (args[2] == "true") {
150 prod = true;
151 } else if (args[2] == "false") {
152 prod = false;
153 } else {
154 fprintf(stderr, "production: must be 'true' or 'false'\n");
155 return -1;
156 }
157 res = ese_boot_set_production(session, prod);
158 if (res == ESE_APP_RESULT_OK) {
159 printf("production mode changed\n");
160 return 0;
161 }
162 fprintf(stderr, "production: failed to change (%.8x)\n", res);
163 return 1;
164}
165
166int handle_state(struct EseBootSession *session, std::vector<std::string> &args) {
167 EseAppResult res;
168 if (args[1] != "get") {
169 fprintf(stderr, "state: unknown command '%s'\n", args[2].c_str());
170 return -1;
171 }
172 // Read in the hex unlockToken and hope for the best.
173 std::vector<uint8_t> data;
174 data.resize(8192);
175 uint16_t len = static_cast<uint16_t>(data.size());
176 res = ese_boot_get_state(session, data.data(), len);
177 if (res != ESE_APP_RESULT_OK) {
178 fprintf(stderr, "state: failed (%.8x)\n", res);
179 return 1;
180 }
181 // TODO: ese_boot_get_state should guarantee length is safe...
182 len = (data[1] << 8) | (data[2]) + 3;
183 printf("Boot Storage State:\n ");
184 print_hexdump(data.data(), 3, len);
185 return 0;
186}
187
Will Drewry92973c72017-04-02 15:04:32 -0500188static const uint8_t auto_data[] = {
189 // lastNonce
190 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x40,
191 // deviceData
192 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
193 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
194 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
195 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
196 // Version
197 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
198 // Nonce
199 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
200 // Signature
Will Drewryd209f712017-04-07 16:03:37 -0500201 0x68, 0x86, 0x9a, 0x16, 0xca, 0x62, 0xea, 0xa9,
202 0x9b, 0xa0, 0x51, 0x03, 0xa6, 0x00, 0x3f, 0xe8,
203 0xf1, 0x43, 0xe6, 0xb7, 0xde, 0x76, 0xfe, 0x21,
204 0x65, 0x87, 0x78, 0xe5, 0x1d, 0x11, 0x6a, 0xe1,
205 0x7b, 0xc6, 0x2e, 0xe2, 0x96, 0x25, 0x48, 0xa7,
206 0x09, 0x43, 0x2c, 0xfd, 0x28, 0xa9, 0x66, 0x8a,
207 0x09, 0xd5, 0x83, 0x3b, 0xde, 0x18, 0x5d, 0xef,
208 0x50, 0x12, 0x8a, 0x8d, 0xfb, 0x2d, 0x46, 0x20,
209 0x69, 0x55, 0x4e, 0x86, 0x63, 0xf6, 0x10, 0xe3,
210 0x59, 0x3f, 0x55, 0x72, 0x18, 0xcb, 0x60, 0x80,
211 0x0d, 0x2e, 0x2f, 0xfc, 0xc2, 0xbf, 0xda, 0x3f,
212 0x4f, 0x2b, 0x6b, 0xf1, 0x5d, 0x28, 0x6b, 0x2b,
213 0x9b, 0x92, 0xf3, 0x4e, 0xf2, 0xb6, 0x23, 0x8e,
214 0x50, 0x64, 0xf6, 0xee, 0xc7, 0x78, 0x6a, 0xe0,
215 0xed, 0xce, 0x2c, 0x1f, 0x0a, 0x47, 0x43, 0x5c,
216 0xe4, 0x69, 0xc5, 0xc1, 0xf9, 0x52, 0x8c, 0xed,
217 0xfd, 0x71, 0x8f, 0x9a, 0xde, 0x62, 0xfc, 0x21,
218 0x07, 0xf9, 0x5f, 0xe1, 0x1e, 0xdc, 0x65, 0x95,
219 0x15, 0xc8, 0xe7, 0xf2, 0xce, 0xa9, 0xd0, 0x55,
220 0xf1, 0x18, 0x89, 0xae, 0xe8, 0x47, 0xd8, 0x8a,
221 0x1f, 0x68, 0xa8, 0x6f, 0x5e, 0x5c, 0xda, 0x3d,
222 0x98, 0xeb, 0x82, 0xf8, 0x1f, 0x7a, 0x43, 0x6d,
223 0x3a, 0x7c, 0x36, 0x76, 0x4f, 0x55, 0xa4, 0x55,
224 0x5f, 0x52, 0x47, 0xa5, 0x71, 0x17, 0x7b, 0x73,
225 0xaa, 0x5c, 0x85, 0x94, 0xb6, 0xe2, 0x37, 0x1f,
226 0x22, 0x29, 0x46, 0x59, 0x20, 0x1f, 0x49, 0x36,
227 0x50, 0xa9, 0x60, 0x5d, 0xeb, 0x99, 0x3f, 0x92,
228 0x31, 0xa0, 0x1d, 0xad, 0xdb, 0xde, 0x40, 0xf6,
229 0xaf, 0x9c, 0x36, 0xe4, 0x0c, 0xf4, 0xcc, 0xaf,
230 0x9f, 0x8b, 0xf9, 0xe6, 0x12, 0x53, 0x4e, 0x58,
231 0xeb, 0x9a, 0x57, 0x08, 0x89, 0xa5, 0x4f, 0x7c,
232 0xb9, 0x78, 0x07, 0x02, 0x17, 0x2c, 0xce, 0xb8,
Will Drewry92973c72017-04-02 15:04:32 -0500233};
234
235int handle_verify_key(struct EseBootSession *session, std::vector<std::string> &args) {
236 EseAppResult res;
237 if (args[1] != "test" && args[1] != "auto") {
238 fprintf(stderr, "verify-key: unknown command '%s'\n", args[2].c_str());
239 return -1;
240 }
241 // Read in the hex unlockToken and hope for the best.
242 std::vector<uint8_t> data;
243 uint16_t len;
244 if (args[1] == "test") {
245 len = hexify(args[2], &data);
246 const uint16_t kExpectedLength = (sizeof(uint64_t) * 2 + sizeof(uint64_t) + 32 + 256);
247 if (len != kExpectedLength) {
248 fprintf(stderr, "verify-key: expected blob of length %hu not %hu\n", kExpectedLength, len);
249 fprintf(stderr, "verify-key: format is as follows (in hex):\n");
250 fprintf(stderr, "[lastNonce:8][deviceData:32][version:8][unlockNonce:8][RSA-SHA256PKCS#1 Signature:256]\n");
251 return 2;
252 }
253 } else {
254 len = sizeof(auto_data);
255 data.assign(&auto_data[0], auto_data + sizeof(auto_data));
256 }
257 printf("verify-key: sending the following test data:\n");
258 print_hexdump(data.data(), 0, data.size());
259 res = ese_boot_carrier_lock_test(session, data.data(), len);
260 if (res == ESE_APP_RESULT_OK) {
261 printf("verified\n");
262 return 0;
263 }
264 printf("failed to verify (%.8x)\n", res);
265 return 1;
266}
267
268int handle_lock_state(struct EseBootSession *session, std::vector<std::string> &args) {
269 EseAppResult res;
270 EseBootLockId lockId;
271 uint16_t lockMetaLen = 0;
Will Drewry5c2c3b92017-05-09 12:31:10 -0500272 uint8_t lockMeta[kEseBootOwnerKeyMax + 1];
Will Drewry80558582017-04-18 13:44:43 -0500273 if (args[1] == "reset") {
274 // No work.
275 } else if (args[2] == "carrier") {
Will Drewry92973c72017-04-02 15:04:32 -0500276 lockId = kEseBootLockIdCarrier;
277 } else if (args[2] == "device") {
278 lockId = kEseBootLockIdDevice;
279 } else if (args[2] == "boot") {
280 lockId = kEseBootLockIdBoot;
281 } else if (args[2] == "owner") {
282 lockId = kEseBootLockIdOwner;
283 } else {
284 fprintf(stderr, "lock: unknown lock '%s'\n", args[2].c_str());
285 return 1;
286 }
287
288 if (args[1] == "get") {
289 uint8_t lockVal = 0;
290 if (lockId == kEseBootLockIdCarrier ||
291 lockId == kEseBootLockIdOwner) {
292 res = ese_boot_lock_xget(session, lockId, lockMeta,
293 sizeof(lockMeta), &lockMetaLen);
294 } else {
295 res = ese_boot_lock_get(session, lockId, &lockVal);
296 }
297 if (res == ESE_APP_RESULT_OK) {
298 if (lockMetaLen > 0) {
299 lockVal = lockMeta[0];
300 }
301 printf("%.2x\n", lockVal);
302 if (lockMetaLen > 0) {
303 print_hexdump(&lockMeta[1], 0, lockMetaLen - 1);
304 }
305 return 0;
306 }
307 fprintf(stderr, "lock: failed to get '%s' (%.8x)\n", args[2].c_str(), res);
308 handle_error(session->ese, res);
309 return 2;
Will Drewry80558582017-04-18 13:44:43 -0500310 } else if (args[1] == "reset") {
311 res = ese_boot_reset_locks(session);
312 if (res == ESE_APP_RESULT_OK) {
313 printf("done.\n");
314 return 0;
315 } else {
316 fprintf(stderr, "lock: failed to reset (%.8x)\n", res);
317 handle_error(session->ese, res);
318 return 3;
319 }
Will Drewry92973c72017-04-02 15:04:32 -0500320 } else if (args[1] == "set") {
321 if (args.size() < 4) {
322 fprintf(stderr, "lock set: not enough arguments supplied\n");
323 return 2;
324 }
325 uint8_t lockVal = static_cast<uint8_t>(std::stoi(args[3], nullptr, 0));
326 if (lockId == kEseBootLockIdCarrier) {
327 res = ESE_APP_RESULT_ERROR_UNCONFIGURED;
328 if (lockVal != 0) {
329 std::string device_data;
330 device_data.push_back(lockVal);
331 if (!collect_device_data(args[4], &device_data)) {
332 fprintf(stderr, "carrier set 1: failed to aggregate device data\n");
333 return 3;
334 }
335 printf("Setting carrier lock with '");
336 for (std::string::iterator it = device_data.begin();
337 it != device_data.end(); ++it) {
Will Drewrye0925682017-04-18 10:35:21 -0500338 if (isprint(*it)) {
339 printf("%c", *it);
340 } else {
341 printf("[0x%.2x]", *it);
342 }
Will Drewry92973c72017-04-02 15:04:32 -0500343 }
344 printf("'\n");
345 const uint8_t *data = reinterpret_cast<const uint8_t *>(device_data.data());
346 res = ese_boot_lock_xset(session, lockId, data, device_data.length());
347 } else {
348 // Read in the hex unlockToken and hope for the best.
349 std::vector<uint8_t> data;
350 data.push_back(lockVal);
351 uint16_t len = hexify(args[4], &data);
352 if (len == 1) {
353 fprintf(stderr, "lock: carrier unlock requires a token\n");
354 return 5;
355 }
356 printf("Passing an unlockToken of length %d to the eSE\n", len - 1);
357 res = ese_boot_lock_xset(session, lockId, data.data(), len);
358 }
359 } else if (lockId == kEseBootLockIdOwner && lockVal != 0) {
360 std::vector<uint8_t> data;
361 data.push_back(lockVal);
362 uint16_t len = hexify(args[4], &data);
363 res = ese_boot_lock_xset(session, lockId, data.data(), len);
364 } else {
365 res = ese_boot_lock_set(session, lockId, lockVal);
366 }
367 if (res != ESE_APP_RESULT_OK) {
368 fprintf(stderr, "lock: failed to set %s state (%.8x)\n",
369 args[2].c_str(), res);
370 handle_error(session->ese, res);
371 return 4;
372 }
373 return 0;
374 }
375 fprintf(stderr, "lock: invalid command\n");
376 return -1;
377}
378
379int handle_rollback(struct EseBootSession *session, std::vector<std::string> &args) {
380 int index = std::stoi(args[2], nullptr, 0);
381 uint8_t slot = static_cast<uint8_t>(index & 0xff);
382 if (slot > 7) {
383 fprintf(stderr, "rollback: slot must be one of [0-7]\n");
384 return 2;
385 }
386
387 uint64_t value = 0;
388 if (args.size() > 3) {
389 unsigned long long conv = std::stoull(args[3], nullptr, 0);
390 value = static_cast<uint64_t>(conv);
391 }
392
393 EseAppResult res;
394 if (args[1] == "get") {
395 res = ese_boot_rollback_index_read(session, slot, &value);
396 if (res != ESE_APP_RESULT_OK) {
397 fprintf(stderr, "rollback: failed to read slot %2x (%.8x)\n",
398 slot, res);
399 handle_error(session->ese, res);
400 return 3;
401 }
402 printf("%" PRIu64 "\n", value);
403 return 0;
404 } else if (args[1] == "set") {
405 res = ese_boot_rollback_index_write(session, slot, value);
406 if (res != ESE_APP_RESULT_OK) {
407 fprintf(stderr, "rollback: failed to write slot %2x (%.8x)\n",
408 slot, res);
409 handle_error(session->ese, res);
410 return 4;
411 }
412 return 0;
413 }
414 fprintf(stderr, "rollback: unknown command '%s'\n", args[1].c_str());
415 return -1;
416}
417
418int handle_args(struct EseBootSession *session, const char *prog, std::vector<std::string> &args) {
419 if (args[0] == "rollback") {
420 return handle_rollback(session, args);
421 } else if (args[0] == "lock") {
422 return handle_lock_state(session, args);
423 } else if (args[0] == "verify-key") {
424 return handle_verify_key(session, args);
425 } else if (args[0] == "production") {
426 return handle_production(session, args);
427 } else if (args[0] == "state") {
428 return handle_state(session, args);
429 } else {
430 usage(prog);
431 return 1;
432 }
433 return 0;
434}
435
436int main(int argc, char **argv) {
437 if (argc < 3) {
438 usage(argv[0]);
439 return 1;
440 }
441 // TODO(wad): move main to a class so we can just dep inject the hw.
442 struct EseInterface ese = ESE_INITIALIZER(ESE_HW_NXP_PN80T_NQ_NCI);
Will Drewry2c8c5d52017-04-17 10:12:03 -0500443 if (ese_open(&ese, nullptr) != 0) {
444 fprintf(stderr, "failed to open device\n");
445 return 2;
446 }
Will Drewry92973c72017-04-02 15:04:32 -0500447 EseBootSession session;
448 ese_boot_session_init(&session);
449 EseAppResult res = ese_boot_session_open(&ese, &session);
450 if (res != ESE_APP_RESULT_OK) {
451 fprintf(stderr, "failed to initiate session (%.8x)\n", res);
452 handle_error(ese, res);
Will Drewry5c2c3b92017-05-09 12:31:10 -0500453 ese_close(&ese);
Will Drewry92973c72017-04-02 15:04:32 -0500454 return 1;
455 }
456 std::vector<std::string> args;
457 args.assign(argv + 1, argv + argc);
458 int ret = handle_args(&session, argv[0], args);
459
460 res = ese_boot_session_close(&session);
461 if (res != ESE_APP_RESULT_OK) {
462 handle_error(&ese, res);
463 }
464 ese_close(&ese);
465 return ret;
466}