1 /*
2 * Copyright (C) 2021 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
17 #include <apploader/cbor.h>
18 #include <apploader/cose.h>
19 #include <dice/cbor_reader.h>
20 #include <dice/cbor_writer.h>
21 #include <endian.h>
22 #include <fcntl.h>
23 #include <getopt.h>
24 #include <interface/apploader/apploader_package.h>
25 #include <inttypes.h>
26 #ifdef WITH_APPLOADER_POLICY_ENGINE
27 #include <lib/apploader_policy_engine/apploader_policy_engine.h>
28 #endif
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <uapi/err.h>
34 #include <unistd.h>
35 #include <array>
36 #include <fstream>
37 #include <memory>
38 #include <sstream>
39 #include <string>
40 #include <vector>
41
42 #include "../app_manifest_parser.h"
43
44 #ifdef WITH_APPLOADER_POLICY_ENGINE
45 /* Global variable to support apploader policy mocking */
46 bool host_mock_state_app_loading_unlocked = false;
47 #endif
48
49 enum class Mode {
50 UNKNOWN,
51 BUILD,
52 SIGN,
53 VERIFY,
54 ENCRYPT,
55 DECRYPT,
56 INFO,
57 #ifdef WITH_APPLOADER_POLICY_ENGINE
58 KEYS,
59 #endif
60 };
61
62 static Mode mode = Mode::UNKNOWN;
63 static bool strict = false;
64
65 static const char* _sopts = "hm:s";
66 static const struct option _lopts[] = {
67 {"help", no_argument, 0, 'h'},
68 {"mode", required_argument, 0, 'm'},
69 {"strict", no_argument, 0, 's'},
70 {0, 0, 0, 0},
71 };
72
print_usage_and_exit(const char * prog,int code)73 static void print_usage_and_exit(const char* prog, int code) {
74 fprintf(stderr, "Usage:\n");
75 fprintf(stderr, "\t%s --mode <mode> [options] ...\n", prog);
76 fprintf(stderr, "\t%s --mode build [options] <output> <ELF> <manifest>\n",
77 prog);
78 fprintf(stderr,
79 "\t%s --mode sign [options] <output> <input> <key> <key id>\n",
80 prog);
81 fprintf(stderr, "\t%s --mode verify [options] <input> <key>\n", prog);
82 fprintf(stderr,
83 "\t%s --mode encrypt [options] <output> <input> <key> <key id>\n",
84 prog);
85 fprintf(stderr, "\t%s --mode decrypt [options] <output> <input> <key>\n",
86 prog);
87 fprintf(stderr, "\t%s --mode info [options] <input>\n", prog);
88 #ifdef WITH_APPLOADER_POLICY_ENGINE
89 fprintf(stderr, "\t%s --mode keys\n", prog);
90 #endif
91 fprintf(stderr, "\n");
92 fprintf(stderr, "Options:\n");
93 fprintf(stderr, "\t-h, --help prints this message and exit\n");
94 fprintf(stderr,
95 "\t-m, --mode mode; one of: build, sign, verify, encrypt, decrypt, info\n");
96 #ifdef WITH_APPLOADER_POLICY_ENGINE
97 fprintf(stderr, "\t keys\n");
98
99 #endif
100 fprintf(stderr,
101 "\t-s, --strict verify signature in strict mode\n");
102 fprintf(stderr, "\n");
103 fprintf(stderr, "Exit Code:\n");
104 fprintf(stderr, " 0 on success, otherwise a non-zero error code.\n");
105 fprintf(stderr,
106 "\n"
107 " For info mode, it is considered an error if a package manifest requires\n"
108 " app encryption, but the package is not encrypted.\n");
109 fprintf(stderr, "\n");
110 fprintf(stderr, "Build:\n");
111 fprintf(stderr, " Cipher: %s\n", coseGetCipherAlg());
112 fprintf(stderr, " Signing: %s\n", coseGetSigningDsa());
113 fprintf(stderr, "\n");
114 exit(code);
115 }
116
parse_options(int argc,char ** argv)117 static void parse_options(int argc, char** argv) {
118 int c;
119 int oidx = 0;
120
121 while (1) {
122 c = getopt_long(argc, argv, _sopts, _lopts, &oidx);
123 if (c == -1) {
124 break; /* done */
125 }
126
127 switch (c) {
128 case 'h':
129 print_usage_and_exit(argv[0], EXIT_SUCCESS);
130 break;
131
132 case 'm':
133 if (!strcmp(optarg, "build")) {
134 mode = Mode::BUILD;
135 } else if (!strcmp(optarg, "sign")) {
136 mode = Mode::SIGN;
137 } else if (!strcmp(optarg, "verify")) {
138 mode = Mode::VERIFY;
139 } else if (!strcmp(optarg, "encrypt")) {
140 mode = Mode::ENCRYPT;
141 } else if (!strcmp(optarg, "decrypt")) {
142 mode = Mode::DECRYPT;
143 } else if (!strcmp(optarg, "info")) {
144 mode = Mode::INFO;
145 #ifdef WITH_APPLOADER_POLICY_ENGINE
146 } else if (!strcmp(optarg, "keys")) {
147 mode = Mode::KEYS;
148 #endif
149 } else {
150 fprintf(stderr, "Unrecognized command mode: %s\n", optarg);
151 /*
152 * Set the mode to UNKNOWN so main prints the usage and exits
153 */
154 mode = Mode::UNKNOWN;
155 }
156 break;
157
158 case 's':
159 strict = true;
160 break;
161
162 default:
163 print_usage_and_exit(argv[0], EXIT_FAILURE);
164 }
165 }
166 }
167
string_to_vector(std::string s)168 static std::vector<uint8_t> string_to_vector(std::string s) {
169 auto* start_ptr = reinterpret_cast<uint8_t*>(s.data());
170 return {start_ptr, start_ptr + s.size()};
171 }
172
read_entire_file(const char * file_name)173 static std::vector<uint8_t> read_entire_file(const char* file_name) {
174 /*
175 * Disable synchronization between C++ streams and FILE* functions for a
176 * performance boost
177 */
178 std::ios::sync_with_stdio(false);
179
180 std::ifstream ifs(file_name, std::ios::in | std::ios::binary);
181 if (!ifs || !ifs.is_open()) {
182 fprintf(stderr, "Failed to open file '%s'\n", file_name);
183 exit(EXIT_FAILURE);
184 }
185
186 std::ostringstream ss;
187 ss << ifs.rdbuf();
188 if (!ss) {
189 fprintf(stderr, "Failed to read file '%s'\n", file_name);
190 exit(EXIT_FAILURE);
191 }
192
193 return string_to_vector(ss.str());
194 }
195
write_entire_file(const char * file_name,const std::vector<uint8_t> & data)196 static void write_entire_file(const char* file_name,
197 const std::vector<uint8_t>& data) {
198 /*
199 * Disable synchronization between C++ streams and FILE* functions for a
200 * performance boost
201 */
202 std::ios::sync_with_stdio(false);
203
204 std::ofstream ofs(file_name,
205 std::ios::out | std::ios::binary | std::ios::trunc);
206 if (!ofs || !ofs.is_open()) {
207 fprintf(stderr, "Failed to create file '%s'\n", file_name);
208 exit(EXIT_FAILURE);
209 }
210
211 ofs.write(reinterpret_cast<const char*>(data.data()), data.size());
212 if (!ofs) {
213 fprintf(stderr, "Failed to write to file '%s'\n", file_name);
214 exit(EXIT_FAILURE);
215 }
216 }
217
build_package(const char * output_path,const char * elf_path,const char * manifest_path)218 static void build_package(const char* output_path,
219 const char* elf_path,
220 const char* manifest_path) {
221 auto elf = read_entire_file(elf_path);
222 auto manifest = read_entire_file(manifest_path);
223
224 cbor::VectorCborEncoder encoded_package;
225 encoded_package.encodeTag(APPLOADER_PACKAGE_CBOR_TAG_APP, [&](auto& enc) {
226 enc.encodeArray([&](auto& enc) {
227 enc.encodeUint(APPLOADER_PACKAGE_FORMAT_VERSION_CURRENT);
228 enc.encodeMap([&](auto& enc) { /* no elements */ });
229 enc.encodeBstr(elf);
230 enc.encodeBstr(manifest);
231 });
232 });
233
234 write_entire_file(output_path, encoded_package.intoVec());
235 }
236
parse_key_id(const char * key_id)237 static uint8_t parse_key_id(const char* key_id) {
238 std::string key_id_str{key_id};
239 size_t key_id_end;
240 int int_key_id = std::stoi(key_id_str, &key_id_end);
241 if (key_id_end < key_id_str.size()) {
242 fprintf(stderr, "Invalid key id: %s\n", key_id);
243 exit(EXIT_FAILURE);
244 }
245 if (int_key_id < std::numeric_limits<uint8_t>::min() ||
246 int_key_id > std::numeric_limits<uint8_t>::max()) {
247 fprintf(stderr, "Key id out of range: %d\n", int_key_id);
248 exit(EXIT_FAILURE);
249 }
250 return static_cast<uint8_t>(int_key_id);
251 }
252
sign_package(const char * output_path,const char * input_path,const char * key_path,uint8_t key_id)253 static void sign_package(const char* output_path,
254 const char* input_path,
255 const char* key_path,
256 uint8_t key_id) {
257 auto input = read_entire_file(input_path);
258 if (coseIsSigned(input, nullptr)) {
259 fprintf(stderr, "Input file is already signed\n");
260 exit(EXIT_FAILURE);
261 }
262
263 cbor::VectorCborEncoder enc;
264 enc.encodeMap([&](auto& enc) {
265 enc.encodeKeyValue(COSE_LABEL_ALG, COSE_VAL_SIGN_ALG);
266 enc.encodeKeyValue(COSE_LABEL_TRUSTY, [&](auto& enc) {
267 enc.encodeArray([&](auto& enc) {
268 enc.encodeTstr("TrustyApp");
269 enc.encodeUint(APPLOADER_SIGNATURE_FORMAT_VERSION_CURRENT);
270 });
271 });
272 });
273
274 auto key = read_entire_file(key_path);
275 CoseByteView protectedHeadersView = enc.view();
276 std::span<const uint8_t> unprotectedHeadersView;
277 auto sig = coseSignEcDsa(key, key_id, input, protectedHeadersView,
278 unprotectedHeadersView, true, true);
279 if (!sig) {
280 fprintf(stderr, "Failed to sign package\n");
281 exit(EXIT_FAILURE);
282 }
283
284 auto full_sig = sig.value();
285 full_sig.insert(full_sig.end(), input.begin(), input.end());
286 write_entire_file(output_path, full_sig);
287 }
288
verify_package(const char * input_path,const char * key_path)289 static void verify_package(const char* input_path, const char* key_path) {
290 auto input = read_entire_file(input_path);
291 size_t signature_length;
292 if (!coseIsSigned(input, &signature_length)) {
293 fprintf(stderr, "Input file is not signed\n");
294 exit(EXIT_FAILURE);
295 }
296
297 auto key = read_entire_file(key_path);
298 bool signature_ok;
299 if (strict) {
300 auto get_key = [&key](uint8_t key_id)
301 -> std::tuple<std::unique_ptr<uint8_t[]>, size_t> {
302 auto key_data = std::make_unique<uint8_t[]>(key.size());
303 if (!key_data) {
304 return {};
305 }
306
307 memcpy(key_data.get(), key.data(), key.size());
308 return {std::move(key_data), key.size()};
309 };
310 signature_ok = strictCheckEcDsaSignature(input.data(), input.size(),
311 get_key, nullptr, nullptr);
312 } else {
313 std::vector<uint8_t> payload(input.begin() + signature_length,
314 input.end());
315 input.resize(signature_length);
316 signature_ok = coseCheckEcDsaSignature(input, payload, key);
317 }
318
319 if (!signature_ok) {
320 fprintf(stderr, "Signature verification failed\n");
321 exit(EXIT_FAILURE);
322 }
323
324 fprintf(stderr, "Signature verification passed\n");
325 }
326
327 struct ContentIsCoseEncrypt {
328 struct CborOut cursor;
329 bool value;
330 };
331
find_content_is_cose_encrypt(const CoseByteView & headers)332 static std::optional<ContentIsCoseEncrypt> find_content_is_cose_encrypt(
333 const CoseByteView& headers) {
334 struct CborIn in;
335 size_t num_headers;
336 uint64_t label;
337 CborInInit(headers.data(), headers.size(), &in);
338 CborReadMap(&in, &num_headers);
339 std::optional<ContentIsCoseEncrypt> res;
340 for (size_t i = 0; i < num_headers; i++) {
341 if (CborReadUint(&in, &label) != CBOR_READ_RESULT_OK) {
342 fprintf(stderr, "Invalid COSE header label.\n");
343 exit(EXIT_FAILURE);
344 }
345
346 if (label == APPLOADER_PACKAGE_HEADER_LABEL_CONTENT_IS_COSE_ENCRYPT) {
347 if (res.has_value()) {
348 fprintf(stderr,
349 "Duplicate content_is_cose_encrypt header fields\n");
350 exit(EXIT_FAILURE);
351 }
352
353 /*
354 * CborIn and CborOut may be layout compatible but we should not
355 * assume that will always be true so copy each field explicitly.
356 */
357 struct CborOut cursor = {.buffer = (uint8_t*)in.buffer,
358 .buffer_size = in.buffer_size,
359 .cursor = in.cursor};
360 auto val = cbor::readCborBoolean(in);
361 if (!val.has_value()) {
362 fprintf(stderr,
363 "Invalid value for content_is_cose_encrypt header\n");
364 exit(EXIT_FAILURE);
365 }
366
367 res = {cursor, *val};
368 } else if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
369 fprintf(stderr, "Failed to parse COSE headers\n");
370 exit(EXIT_FAILURE);
371 }
372 }
373
374 return res;
375 }
376
update_header_content_is_cose_encrypt(std::vector<uint8_t> & headers,bool new_value)377 static void update_header_content_is_cose_encrypt(std::vector<uint8_t>& headers,
378 bool new_value) {
379 auto content_is_cose_encrypt =
380 find_content_is_cose_encrypt({headers.data(), headers.size()});
381 if (content_is_cose_encrypt.has_value()) {
382 if (content_is_cose_encrypt->value == new_value) {
383 fprintf(stderr, "Invalid content_is_cose_encrypt value\n");
384 exit(EXIT_FAILURE);
385 }
386
387 // Update the content flag
388 if (new_value) {
389 CborWriteTrue(&content_is_cose_encrypt->cursor);
390 } else {
391 CborWriteFalse(&content_is_cose_encrypt->cursor);
392 }
393 assert(!CborOutOverflowed(&content_is_cose_encrypt->cursor));
394 } else if (new_value) {
395 cbor::VectorCborEncoder enc;
396 enc.encodeMap([&](auto& enc) {
397 enc.encodeKeyValue(
398 APPLOADER_PACKAGE_HEADER_LABEL_CONTENT_IS_COSE_ENCRYPT,
399 true);
400 });
401 const auto newHeaders = enc.view();
402
403 auto updatedHeaders =
404 cbor::mergeMaps({headers.data(), headers.size()}, newHeaders);
405 assert(updatedHeaders.has_value() && "Failed to update COSE headers");
406
407 headers.assign(updatedHeaders->begin(), updatedHeaders->end());
408 }
409 }
410
411 struct PackageInfo {
412 // Application package format version
413 uint64_t version;
414
415 // Application metadata as a map of headers
416 CoseByteView headers;
417
418 // ELF image or COSE_Encrypt structure
419 CoseByteView elf_item;
420
421 // Application manifest
422 CoseByteView manifest;
423 };
424
parse_cose_recipient(struct CborIn * in,struct PackageInfo * package)425 static void parse_cose_recipient(struct CborIn* in,
426 struct PackageInfo* package) {
427 size_t num_elements, num_pairs;
428
429 if (CborReadArray(in, &num_elements) != CBOR_READ_RESULT_OK) {
430 fprintf(stderr,
431 "Failed to read COSE_Recipient "
432 "from COSE encryption structure\n");
433 exit(EXIT_FAILURE);
434 }
435
436 if (num_elements != 3) {
437 fprintf(stderr, "Invalid COSE_Recipient array size, got %zu\n",
438 num_elements);
439 exit(EXIT_FAILURE);
440 }
441
442 const uint8_t* enc_protected_headers_data;
443 size_t enc_protected_headers_size;
444 if (CborReadBstr(in, &enc_protected_headers_size,
445 &enc_protected_headers_data) != CBOR_READ_RESULT_OK) {
446 fprintf(stderr,
447 "Invalid COSE_Recipient. "
448 "Encrypted protected headers is not a binary string\n");
449 exit(EXIT_FAILURE);
450 }
451
452 if (CborReadMap(in, &num_pairs) != CBOR_READ_RESULT_OK) {
453 fprintf(stderr, "Invalid COSE_Recipient. Failed to read map\n");
454 exit(EXIT_FAILURE);
455 }
456
457 for (size_t i = 0; i < 2 * num_pairs; i++) {
458 if (CborReadSkip(in) != CBOR_READ_RESULT_OK) {
459 fprintf(stderr,
460 "Invalid COSE_Recipient. Failed to skip map element\n");
461 exit(EXIT_FAILURE);
462 }
463 }
464
465 const uint8_t* ciphertext_data;
466 size_t ciphertext_size;
467 if (CborReadBstr(in, &ciphertext_size, &ciphertext_data) !=
468 CBOR_READ_RESULT_OK) {
469 fprintf(stderr,
470 "Invalid COSE_Recipient. "
471 "Ciphertext is not a binary string\n");
472 exit(EXIT_FAILURE);
473 }
474 }
475
parse_package(std::span<const uint8_t> input,bool check_sign_tag)476 static PackageInfo parse_package(std::span<const uint8_t> input,
477 bool check_sign_tag) {
478 struct CborIn in;
479 uint64_t tag;
480 size_t num_elements, num_pairs;
481 struct PackageInfo package;
482
483 CborInInit(input.data(), input.size(), &in);
484
485 if (CborReadTag(&in, &tag) != CBOR_READ_RESULT_OK) {
486 fprintf(stderr, "Failed to parse input file as CBOR\n");
487 exit(EXIT_FAILURE);
488 }
489
490 if (check_sign_tag && tag == COSE_TAG_SIGN1) {
491 fprintf(stderr, "Input file is already signed\n");
492 exit(EXIT_FAILURE);
493 }
494
495 if (tag != APPLOADER_PACKAGE_CBOR_TAG_APP) {
496 fprintf(stderr, "Input file is not a Trusty application package\n");
497 exit(EXIT_FAILURE);
498 }
499
500 if (CborReadArray(&in, &num_elements) != CBOR_READ_RESULT_OK) {
501 fprintf(stderr, "Invalid input file format\n");
502 exit(EXIT_FAILURE);
503 }
504
505 if (num_elements != APPLOADER_PACKAGE_CBOR_ARRAY_SZ) {
506 fprintf(stderr, "Invalid number of CBOR array elements: %zd\n",
507 num_elements);
508 exit(EXIT_FAILURE);
509 }
510
511 if (CborReadUint(&in, &package.version) != CBOR_READ_RESULT_OK) {
512 fprintf(stderr, "Invalid input file format\n");
513 exit(EXIT_FAILURE);
514 }
515
516 if (package.version != APPLOADER_PACKAGE_FORMAT_VERSION_CURRENT) {
517 fprintf(stderr,
518 "Invalid package version, expected %" PRIu64 " got %" PRIu64
519 "\n",
520 APPLOADER_PACKAGE_FORMAT_VERSION_CURRENT, package.version);
521 exit(EXIT_FAILURE);
522 }
523
524 const size_t headers_offset = CborInOffset(&in);
525
526 if (CborReadMap(&in, &num_pairs) != CBOR_READ_RESULT_OK) {
527 fprintf(stderr, "Invalid input file format\n");
528 exit(EXIT_FAILURE);
529 }
530
531 uint64_t label;
532 bool content_is_cose_encrypt = false;
533 for (size_t i = 0; i < num_pairs; i++) {
534 // read key
535 if (CborReadUint(&in, &label) != CBOR_READ_RESULT_OK) {
536 fprintf(stderr, "Invalid package headers\n");
537 exit(EXIT_FAILURE);
538 }
539
540 // read value
541 switch (label) {
542 case APPLOADER_PACKAGE_HEADER_LABEL_CONTENT_IS_COSE_ENCRYPT: {
543 auto val = cbor::readCborBoolean(in);
544 if (!val.has_value()) {
545 fprintf(stderr, "Invalid value for content_is_cose_encrypt\n");
546 exit(EXIT_FAILURE);
547 }
548 content_is_cose_encrypt = *val;
549 break;
550 }
551
552 default:
553 fprintf(stderr,
554 "Package headers contain invalid label: %" PRIu64 "\n",
555 label);
556 exit(EXIT_FAILURE);
557 }
558 }
559
560 const size_t elf_offset = CborInOffset(&in);
561 package.headers = {(const uint8_t*)input.data() + headers_offset,
562 elf_offset - headers_offset};
563
564 const uint8_t* elf_data;
565 size_t elf_size;
566 if (content_is_cose_encrypt) {
567 if (CborReadArray(&in, &num_elements) != CBOR_READ_RESULT_OK) {
568 fprintf(stderr, "Invalid COSE encryption array\n");
569 exit(EXIT_FAILURE);
570 }
571
572 /* content is COSE_Encrypt */
573 if (num_elements < 3 || num_elements > 4) {
574 fprintf(stderr, "Invalid COSE encryption array size, got %zu\n",
575 num_elements);
576 exit(EXIT_FAILURE);
577 }
578
579 const uint8_t* enc_protected_headers_data;
580 size_t enc_protected_headers_size;
581 if (CborReadBstr(&in, &enc_protected_headers_size,
582 &enc_protected_headers_data) != CBOR_READ_RESULT_OK) {
583 fprintf(stderr,
584 "Failed to retrieve protected headers from COSE "
585 "encryption structure\n");
586 exit(EXIT_FAILURE);
587 }
588
589 /* TODO: parse and validate protected headers */
590 if (CborReadMap(&in, &num_pairs) != CBOR_READ_RESULT_OK) {
591 fprintf(stderr,
592 "Failed to retrieve unprotected headers from COSE "
593 "encryption structure\n");
594 exit(EXIT_FAILURE);
595 }
596
597 /* TODO: parse and validate unprotected headers */
598 for (size_t i = 0; i < 2 * num_pairs; i++) {
599 if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
600 fprintf(stderr, "Invalid input file format\n");
601 exit(EXIT_FAILURE);
602 }
603 }
604
605 const uint8_t* ciphertext_data;
606 size_t ciphertext_size;
607 if (CborReadBstr(&in, &ciphertext_size, &ciphertext_data) !=
608 CBOR_READ_RESULT_OK) {
609 fprintf(stderr,
610 "Failed to retrieve ciphertext "
611 "from COSE encryption structure\n");
612 exit(EXIT_FAILURE);
613 }
614
615 if (num_elements == 4) {
616 size_t num_recipients;
617 if (CborReadArray(&in, &num_recipients) != CBOR_READ_RESULT_OK) {
618 fprintf(stderr,
619 "Failed to read recipients array "
620 "from COSE encryption structure\n");
621 exit(EXIT_FAILURE);
622 }
623
624 while (num_recipients--) {
625 parse_cose_recipient(&in, &package);
626 }
627 }
628
629 package.elf_item = {(const uint8_t*)input.data() + elf_offset,
630 CborInOffset(&in) - elf_offset};
631 } else { /* content is unencrypted */
632 if (CborReadBstr(&in, &elf_size, &elf_data) != CBOR_READ_RESULT_OK) {
633 fprintf(stderr,
634 "Failed to read ELF content from application package\n");
635 exit(EXIT_FAILURE);
636 }
637
638 package.elf_item = {elf_data, elf_size};
639 }
640
641 const uint8_t* manifest_data;
642 size_t manifest_size;
643 if (CborReadBstr(&in, &manifest_size, &manifest_data) !=
644 CBOR_READ_RESULT_OK) {
645 fprintf(stderr, "Invalid CBOR type. Failed to read manifest as Bstr\n");
646 exit(EXIT_FAILURE);
647 }
648 package.manifest = {manifest_data, manifest_size};
649
650 assert(CborInAtEnd(&in));
651
652 return package;
653 }
654
encrypt_package(const char * output_path,const char * input_path,const char * key_path,uint8_t key_id)655 static void encrypt_package(const char* output_path,
656 const char* input_path,
657 const char* key_path,
658 uint8_t key_id) {
659 auto input = read_entire_file(input_path);
660 auto pkg_info = parse_package(input, true);
661
662 auto key = read_entire_file(key_path);
663 if (key.size() != kAesGcmKeySize) {
664 fprintf(stderr, "Wrong AES-GCM key size: %zu\n", key.size());
665 exit(EXIT_FAILURE);
666 }
667
668 cbor::VectorCborEncoder enc;
669 enc.encodeMap([&](auto& enc) {
670 enc.encodeKeyValue(COSE_LABEL_ALG, COSE_VAL_CIPHER_ALG);
671 enc.encodeKeyValue(COSE_LABEL_TRUSTY, "TrustyApp");
672 });
673
674 auto encodedProtectedHeaders = enc.intoVec();
675 auto cose_encrypt =
676 coseEncryptAesGcmKeyWrap(key, key_id, pkg_info.elf_item, {},
677 encodedProtectedHeaders, {}, false);
678 if (!cose_encrypt) {
679 fprintf(stderr, "Failed to encrypt ELF file\n");
680 exit(EXIT_FAILURE);
681 }
682
683 std::vector<uint8_t> enc_headers(pkg_info.headers.begin(),
684 pkg_info.headers.end());
685 update_header_content_is_cose_encrypt(enc_headers, true);
686
687 // Build a new encrypted array since the original array has a semantic
688 // tag that we do not want to preserve.
689 enc = cbor::VectorCborEncoder();
690 enc.encodeTag(APPLOADER_PACKAGE_CBOR_TAG_APP, [&](auto& enc) {
691 enc.encodeArray([&](auto& enc) {
692 enc.encodeInt(pkg_info.version);
693 enc.copyBytes(enc_headers);
694 enc.copyBytes(cose_encrypt.value());
695 enc.encodeBstr(pkg_info.manifest);
696 });
697 });
698 auto encoded_package = enc.intoVec();
699 write_entire_file(output_path, encoded_package);
700 }
701
decrypt_package(const char * output_path,const char * input_path,const char * key_path)702 static void decrypt_package(const char* output_path,
703 const char* input_path,
704 const char* key_path) {
705 auto input = read_entire_file(input_path);
706 auto pkg_info = parse_package(input, true);
707
708 auto key = read_entire_file(key_path);
709 if (key.size() != kAesGcmKeySize) {
710 fprintf(stderr, "Wrong AES-GCM key size: %zu, expected %zu\n",
711 key.size(), kAesGcmKeySize);
712 exit(EXIT_FAILURE);
713 }
714
715 auto get_key = [&key](
716 uint8_t key_id) -> std::tuple<std::unique_ptr<uint8_t[]>, size_t> {
717 auto key_data = std::make_unique<uint8_t[]>(key.size());
718 if (!key_data) {
719 return {};
720 }
721
722 memcpy(key_data.get(), key.data(), key.size());
723 return {std::move(key_data), key.size()};
724 };
725
726 const uint8_t* package_start;
727 size_t package_size;
728 if (!coseDecryptAesGcmKeyWrapInPlace(pkg_info.elf_item, get_key, {}, false,
729 &package_start, &package_size)) {
730 fprintf(stderr, "Failed to decrypt ELF file\n");
731 exit(EXIT_FAILURE);
732 }
733
734 std::vector<uint8_t> dec_headers(pkg_info.headers.begin(),
735 pkg_info.headers.end());
736 update_header_content_is_cose_encrypt(dec_headers, false);
737
738 // Build a new decrypted array since the original array has a semantic
739 // tag that we do not want to preserve.
740 cbor::VectorCborEncoder enc;
741 enc.encodeTag(APPLOADER_PACKAGE_CBOR_TAG_APP, [&](auto& enc) {
742 enc.encodeArray([&](auto& enc) {
743 enc.encodeInt(pkg_info.version);
744 enc.copyBytes(dec_headers);
745 enc.encodeBstr({package_start, package_size});
746 enc.copyBytes(pkg_info.manifest);
747 });
748 });
749
750 auto encoded_package = enc.intoVec();
751 write_entire_file(output_path, encoded_package);
752 }
753
print_package_info(const char * input_path)754 static void print_package_info(const char* input_path) {
755 struct apploader_policy_data ap_data = {.force_store_min_version = false};
756 uint8_t package_key_id = UINT8_MAX;
757
758 // We call into some COSE functions to retrieve the
759 // key ids, and we don't want them to print any errors
760 // (which they do since we pass them invalid keys)
761 bool oldSilenceErrors = coseSetSilenceErrors(true);
762
763 printf("File: %s\n", input_path);
764
765 auto input = read_entire_file(input_path);
766 size_t signature_length = 0;
767 if (coseIsSigned(input, &signature_length)) {
768 printf("Signed: YES\n");
769
770 // Call into cose.cpp with a callback that prints and saves the key id
771 auto print_key_id = [&](uint8_t key_id)
772 -> std::tuple<std::unique_ptr<uint8_t[]>, size_t> {
773 printf("Signature key id: %" PRIu8 "\n", key_id);
774 package_key_id = key_id;
775 return {};
776 };
777 strictCheckEcDsaSignature(
778 reinterpret_cast<const uint8_t*>(input.data()), input.size(),
779 print_key_id, nullptr, nullptr);
780 } else {
781 printf("Signed: NO\n");
782 }
783
784 std::span signed_package(input.data() + signature_length,
785 input.size() - signature_length);
786 auto pkg_info = parse_package(signed_package, false);
787 auto content_is_cose_encrypt =
788 find_content_is_cose_encrypt(pkg_info.headers);
789
790 // Get manifest to check encryption requirement
791 if (pkg_info.manifest.size() == 0) {
792 fprintf(stderr, "Package did not contain a valid manifest\n");
793 exit(EXIT_FAILURE);
794 }
795
796 if (!apploader_parse_manifest(
797 reinterpret_cast<const char*>(pkg_info.manifest.data()),
798 pkg_info.manifest.size(), &ap_data.manifest_extracts)) {
799 fprintf(stderr, "Unable to extract manifest fields\n");
800 exit(EXIT_FAILURE);
801 }
802
803 bool package_error = false;
804
805 if (content_is_cose_encrypt && content_is_cose_encrypt->value) {
806 if (ap_data.manifest_extracts.requires_encryption) {
807 printf("Encrypted: YES, REQUIRED\n");
808 } else {
809 printf("Encrypted: YES, OPTIONAL\n");
810 }
811
812 // Call into cose.cpp with a callback that prints the key id
813 auto print_key_id = [
814 ](uint8_t key_id) -> std::tuple<std::unique_ptr<uint8_t[]>, size_t> {
815 printf("Encryption key id: %" PRIu8 "\n", key_id);
816 return {};
817 };
818
819 const uint8_t* package_start;
820 size_t package_size;
821 coseDecryptAesGcmKeyWrapInPlace(pkg_info.elf_item, print_key_id, {},
822 false, &package_start, &package_size);
823 } else if (ap_data.manifest_extracts.requires_encryption) {
824 printf("Encrypted: NO, REQUIRED\n");
825 fprintf(stderr,
826 "Error: app is not encrypted, contrary to manifest requirement.\n");
827 fprintf(stderr,
828 "Either encrypt the app, or remove the manifest requirement.\n");
829 package_error = true;
830 } else {
831 printf("Encrypted: NO, OPTIONAL\n");
832 }
833
834 const uuid_t* uuid = &ap_data.manifest_extracts.uuid;
835 printf("UUID: %08" PRIx32 "-%04" PRIx16 "-%04" PRIx16 "-%02" PRIx8
836 "%02" PRIx8 "-%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8
837 "%02" PRIx8 "%02" PRIx8 "\n",
838 uuid->time_low, uuid->time_mid, uuid->time_hi_and_version,
839 uuid->clock_seq_and_node[0], uuid->clock_seq_and_node[1],
840 uuid->clock_seq_and_node[2], uuid->clock_seq_and_node[3],
841 uuid->clock_seq_and_node[4], uuid->clock_seq_and_node[5],
842 uuid->clock_seq_and_node[6], uuid->clock_seq_and_node[7]);
843
844 printf("Version: %" PRIu32 "\n", ap_data.manifest_extracts.version);
845 printf("Min version: %" PRIu32 "\n", ap_data.manifest_extracts.min_version);
846
847 #ifdef WITH_APPLOADER_POLICY_ENGINE
848 printf("App loading policy checks\n");
849
850 for (int i = 0; i < 2; i++) {
851 bool can_load = false;
852
853 // Set the mocked apploading state
854 host_mock_state_app_loading_unlocked = !i;
855
856 // Check if the key can be retrieved
857 int rc = apploader_policy_engine_get_key(
858 package_key_id, &ap_data.public_key, &ap_data.public_key_size);
859 if (rc != NO_ERROR) {
860 printf("Apploading policy: FAILED, the signing key is invalid.\n");
861 } else {
862 if (!apploader_policy_engine_validate(&ap_data)) {
863 printf("Apploading policy: FAILED, policy did not pass\n");
864 } else {
865 printf("Apploading policy: PASS (apploading %s)\n",
866 host_mock_state_app_loading_unlocked ? "unlocked"
867 : "locked");
868 can_load = true;
869 }
870
871 apploader_policy_engine_put_key(ap_data.public_key);
872 }
873
874 if (!can_load && !host_mock_state_app_loading_unlocked) {
875 fprintf(stderr,
876 "Error: package cannot load on a production device.\n");
877 package_error = true;
878 }
879 }
880 #endif
881 // Restore the old silence flag
882 coseSetSilenceErrors(oldSilenceErrors);
883
884 if (package_error) {
885 fprintf(stderr, "Exiting with error status.\n");
886 exit(EXIT_FAILURE);
887 }
888 }
889
890 #ifdef WITH_APPLOADER_POLICY_ENGINE
print_key_info(void)891 static void print_key_info(void) {
892 for (uint8_t kid = 0; kid < UINT8_MAX; kid++) {
893 const uint8_t* public_key;
894 unsigned int public_key_size;
895
896 if (apploader_policy_engine_get_key(kid, &public_key,
897 &public_key_size) == NO_ERROR) {
898 printf("Key %" PRIu8 "\n", kid);
899
900 apploader_policy_engine_put_key(public_key);
901 }
902 }
903 }
904 #endif
905
main(int argc,char ** argv)906 int main(int argc, char** argv) {
907 parse_options(argc, argv);
908
909 switch (mode) {
910 case Mode::BUILD:
911 if (optind + 3 != argc) {
912 print_usage_and_exit(argv[0], EXIT_FAILURE);
913 }
914 build_package(argv[optind], argv[optind + 1], argv[optind + 2]);
915 break;
916
917 case Mode::SIGN:
918 if (optind + 4 != argc) {
919 print_usage_and_exit(argv[0], EXIT_FAILURE);
920 }
921 sign_package(argv[optind], argv[optind + 1], argv[optind + 2],
922 parse_key_id(argv[optind + 3]));
923 break;
924
925 case Mode::VERIFY:
926 if (optind + 2 != argc) {
927 print_usage_and_exit(argv[0], EXIT_FAILURE);
928 }
929 verify_package(argv[optind], argv[optind + 1]);
930 break;
931
932 case Mode::ENCRYPT:
933 if (optind + 4 != argc) {
934 print_usage_and_exit(argv[0], EXIT_FAILURE);
935 }
936 encrypt_package(argv[optind], argv[optind + 1], argv[optind + 2],
937 parse_key_id(argv[optind + 3]));
938 break;
939
940 case Mode::DECRYPT:
941 if (optind + 3 != argc) {
942 print_usage_and_exit(argv[0], EXIT_FAILURE);
943 }
944 decrypt_package(argv[optind], argv[optind + 1], argv[optind + 2]);
945 break;
946
947 case Mode::INFO:
948 if (optind + 1 != argc) {
949 print_usage_and_exit(argv[0], EXIT_FAILURE);
950 }
951 print_package_info(argv[optind]);
952 break;
953 #ifdef WITH_APPLOADER_POLICY_ENGINE
954 case Mode::KEYS:
955 if (optind != argc) {
956 print_usage_and_exit(argv[0], EXIT_FAILURE);
957 }
958 print_key_info();
959 break;
960 #endif
961 default:
962 print_usage_and_exit(argv[0], EXIT_FAILURE);
963 break;
964 }
965
966 return 0;
967 }
968