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