Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
main.cc
1 #include <boost/program_options.hpp>
2 #include <fstream>
3 #include <iostream>
4 #include <istream>
5 #include <iterator>
6 #include <ostream>
7 #include <string>
8 
9 #include "logging/logging.h"
10 #include "uptane_repo.h"
11 
12 namespace po = boost::program_options;
13 
14 KeyType parseKeyType(const po::variables_map &vm) {
15  std::string key_type_arg = vm["keytype"].as<std::string>();
16  std::istringstream key_type_str{std::string("\"") + key_type_arg + "\""};
17  KeyType key_type;
18  key_type_str >> key_type;
19  return key_type;
20 }
21 
22 int main(int argc, char **argv) {
23  po::options_description desc("uptane-generator command line options");
24  // clang-format off
25  desc.add_options()
26  ("help,h", "print usage")
27  ("command", po::value<std::string>(), "generate: \tgenerate a new repository\n"
28  "adddelegation: \tadd a delegated role to the Image repo metadata\n"
29  "revokedelegation: \tremove delegated role from the Image repo metadata and all signed targets of this role\n"
30  "image: \tadd a target to the Image repo metadata\n"
31  "addtarget: \tprepare Director Targets metadata for a given device\n"
32  "signtargets: \tsign the staged Director Targets metadata\n"
33  "emptytargets: \tclear the staged Director Targets metadata\n"
34  "oldtargets: \tfill the staged Director Targets metadata with what is currently signed\n"
35  "sign: \tsign arbitrary metadata with repo keys\n"
36  "addcampaigns: \tgenerate campaigns json\n"
37  "refresh: \trefresh a metadata object (bump the version)")
38  ("path", po::value<boost::filesystem::path>(), "path to the repository")
39  ("filename", po::value<boost::filesystem::path>(), "path to the image")
40  ("hwid", po::value<std::string>(), "target hardware identifier")
41  ("targetformat", po::value<std::string>(), "format of target for 'image' command")
42  ("targetcustom", po::value<boost::filesystem::path>(), "path to custom JSON for 'image' command")
43  ("serial", po::value<std::string>(), "target ECU serial")
44  ("expires", po::value<std::string>(), "expiration time")
45  ("keyname", po::value<std::string>(), "name of key's role")
46  ("repotype", po::value<std::string>(), "director|image")
47  ("correlationid", po::value<std::string>()->default_value(""), "correlation id")
48  ("keytype", po::value<std::string>()->default_value("RSA2048"), "Uptane key type")
49  ("targetname", po::value<std::string>(), "target's name (if different than filename)")
50  ("targetsha256", po::value<std::string>(), "target's SHA256 hash (for adding metadata without an actual file)")
51  ("targetsha512", po::value<std::string>(), "target's SHA512 hash (for adding metadata without an actual file)")
52  ("targetlength", po::value<uint64_t>(), "target's length (for adding metadata without an actual file)")
53  ("dname", po::value<std::string>(), "delegated role name")
54  ("dterm", po::bool_switch(), "if the created delegated role is terminating")
55  ("dparent", po::value<std::string>()->default_value("targets"), "delegated role parent name")
56  ("dpattern", po::value<std::string>(), "delegated file path pattern")
57  ("url", po::value<std::string>(), "custom download URL");
58 
59  // clang-format on
60 
61  po::positional_options_description positionalOptions;
62  positionalOptions.add("command", 1);
63  positionalOptions.add("path", 1);
64  positionalOptions.add("filename", 1);
65 
66  try {
67  logger_init();
68  logger_set_threshold(boost::log::trivial::info);
69  po::variables_map vm;
70  po::basic_parsed_options<char> parsed_options =
71  po::command_line_parser(argc, argv).options(desc).positional(positionalOptions).run();
72  po::store(parsed_options, vm);
73  po::notify(vm);
74  if (vm.count("help") != 0) {
75  std::cout << desc << std::endl;
76  exit(EXIT_SUCCESS);
77  }
78 
79  if (vm.count("command") != 0 && vm.count("path") != 0) {
80  std::string expiration_time;
81  if (vm.count("expires") != 0) {
82  expiration_time = vm["expires"].as<std::string>();
83  }
84  std::string correlation_id;
85  if (vm.count("correlationid") != 0) {
86  correlation_id = vm["correlationid"].as<std::string>();
87  }
88  std::string dname;
89  if (vm.count("dname") != 0) {
90  dname = vm["dname"].as<std::string>();
91  }
92  std::string command = vm["command"].as<std::string>();
93  boost::filesystem::path repo_dir = vm["path"].as<boost::filesystem::path>();
94  UptaneRepo repo(repo_dir, expiration_time, correlation_id);
95  if (command == "generate") {
96  KeyType key_type = parseKeyType(vm);
97  repo.generateRepo(key_type);
98  std::cout << "Uptane metadata repos generated at " << repo_dir << std::endl;
99  } else if (command == "image") {
100  if (vm.count("targetname") == 0 && vm.count("filename") == 0) {
101  std::cerr << "image command requires --targetname or --filename\n";
102  exit(EXIT_FAILURE);
103  }
104  if (vm.count("hwid") == 0) {
105  std::cerr << "image command requires --hwid\n";
106  exit(EXIT_FAILURE);
107  }
108  auto targetname = (vm.count("targetname") > 0) ? vm["targetname"].as<std::string>()
109  : vm["filename"].as<boost::filesystem::path>();
110  const std::string hwid = vm["hwid"].as<std::string>();
111 
112  Delegation delegation;
113  if (vm.count("dname") != 0) {
114  delegation = Delegation(repo_dir, dname);
115  if (!delegation.isMatched(targetname)) {
116  std::cerr << "Image path doesn't match delegation!\n";
117  exit(EXIT_FAILURE);
118  }
119  std::cout << "Added a target " << targetname << " to a delegated role " << dname << std::endl;
120  }
121  std::string url;
122  if (vm.count("url") != 0) {
123  url = vm["url"].as<std::string>();
124  }
125  if (vm.count("filename") > 0) {
126  repo.addImage(vm["filename"].as<boost::filesystem::path>(), targetname, hwid, url, delegation);
127  std::cout << "Added a target " << targetname << " to the Image repo metadata" << std::endl;
128  } else {
129  if ((vm.count("targetsha256") == 0 && vm.count("targetsha512") == 0) || vm.count("targetlength") == 0) {
130  std::cerr << "image command requires --targetsha256 or --targetsha512, and --targetlength when --filename "
131  "is not supplied.\n";
132  exit(EXIT_FAILURE);
133  }
134  std::unique_ptr<Uptane::Hash> hash;
135  if (vm.count("targetsha256") > 0) {
136  hash = std_::make_unique<Uptane::Hash>(Uptane::Hash::Type::kSha256, vm["targetsha256"].as<std::string>());
137  } else {
138  hash = std_::make_unique<Uptane::Hash>(Uptane::Hash::Type::kSha512, vm["targetsha512"].as<std::string>());
139  }
140  Json::Value custom;
141  if (vm.count("targetcustom") > 0 && vm.count("targetformat") > 0) {
142  std::cerr << "--targetcustom and --targetformat cannot be used together";
143  exit(EXIT_FAILURE);
144  }
145  if (vm.count("targetcustom") > 0) {
146  std::ifstream custom_file(vm["targetcustom"].as<boost::filesystem::path>().c_str());
147  custom_file >> custom;
148  } else if (vm.count("targetformat") > 0) {
149  custom = Json::Value();
150  custom["targetFormat"] = vm["targetformat"].as<std::string>();
151  }
152  repo.addCustomImage(targetname.string(), *hash, vm["targetlength"].as<uint64_t>(), hwid, url, delegation,
153  custom);
154  std::cout << "Added a custom image target " << targetname.string() << std::endl;
155  }
156  } else if (command == "addtarget") {
157  if (vm.count("targetname") == 0 || vm.count("hwid") == 0 || vm.count("serial") == 0) {
158  std::cerr << "addtarget command requires --targetname, --hwid, and --serial\n";
159  exit(EXIT_FAILURE);
160  }
161  const std::string targetname = vm["targetname"].as<std::string>();
162  const std::string hwid = vm["hwid"].as<std::string>();
163  const std::string serial = vm["serial"].as<std::string>();
164  std::string url;
165  if (vm.count("url") != 0) {
166  url = vm["url"].as<std::string>();
167  }
168  repo.addTarget(targetname, hwid, serial, url, expiration_time);
169  std::cout << "Added target " << targetname << " to Director Targets metadata for ECU with serial " << serial
170  << " and hardware ID " << hwid << std::endl;
171  } else if (command == "adddelegation") {
172  if (vm.count("dname") == 0 || vm.count("dpattern") == 0) {
173  std::cerr << "adddelegation command requires --dname and --dpattern\n";
174  exit(EXIT_FAILURE);
175  }
176  std::string dparent = vm["dparent"].as<std::string>();
177  std::string dpattern = vm["dpattern"].as<std::string>();
178  KeyType key_type = parseKeyType(vm);
179  repo.addDelegation(Uptane::Role(dname, true), Uptane::Role(dparent, dparent != "targets"),
180  vm["dpattern"].as<std::string>(), vm["dterm"].as<bool>(), key_type);
181  std::cout << "Added a delegated role " << dname << " with dpattern " << dpattern
182  << " to the Image repo metadata" << std::endl;
183  } else if (command == "revokedelegation") {
184  if (vm.count("dname") == 0) {
185  std::cerr << "revokedelegation command requires --dname\n";
186  exit(EXIT_FAILURE);
187  }
188  repo.revokeDelegation(Uptane::Role(dname, true));
189  std::cout << "Revoked the delegation " << dname << std::endl;
190  } else if (command == "signtargets") {
191  repo.signTargets();
192  std::cout << "Signed the staged Director Targets metadata" << std::endl;
193  } else if (command == "emptytargets") {
194  repo.emptyTargets();
195  std::cout << "Cleared the staged Director Targets metadata" << std::endl;
196  } else if (command == "oldtargets") {
197  repo.oldTargets();
198  std::cout << "Populated the Director Targets metadata with the currently signed metadata" << std::endl;
199  } else if (command == "sign") {
200  if (vm.count("repotype") == 0 || vm.count("keyname") == 0) {
201  std::cerr << "sign command requires --repotype and --keyname\n";
202  exit(EXIT_FAILURE);
203  }
204  std::cin >> std::noskipws;
205  std::istream_iterator<char> it(std::cin);
206  std::istream_iterator<char> end;
207  std::string text_to_sign(it, end);
208 
209  Repo base_repo(Uptane::RepositoryType(vm["repotype"].as<std::string>()), repo_dir, expiration_time,
210  correlation_id);
211 
212  auto json_to_sign = Utils::parseJSON(text_to_sign);
213  if (json_to_sign == Json::nullValue) {
214  std::cerr << "Text to sign must be valid json\n";
215  exit(EXIT_FAILURE);
216  }
217 
218  auto json_signed = base_repo.signTuf(Uptane::Role(vm["keyname"].as<std::string>()), json_to_sign);
219  std::cout << Utils::jsonToCanonicalStr(json_signed);
220  } else if (command == "addcampaigns") {
221  repo.generateCampaigns();
222  std::cout << "Generated campaigns" << std::endl;
223  } else if (command == "refresh") {
224  if (vm.count("repotype") == 0 || vm.count("keyname") == 0) {
225  std::cerr << "refresh command requires --repotype and --keyname\n";
226  exit(EXIT_FAILURE);
227  }
228  repo.refresh(Uptane::RepositoryType(vm["repotype"].as<std::string>()),
229  Uptane::Role(vm["keyname"].as<std::string>()));
230  } else {
231  std::cout << desc << std::endl;
232  exit(EXIT_FAILURE);
233  }
234  } else {
235  std::cout << desc << std::endl;
236  exit(EXIT_FAILURE);
237  }
238 
239  exit(EXIT_SUCCESS);
240 
241  } catch (const po::error &o) {
242  std::cout << o.what() << std::endl;
243  std::cout << desc;
244  } catch (std::exception &ex) {
245  LOG_ERROR << "Exception: " << ex.what();
246  } catch (...) {
247  LOG_ERROR << "Unknown error";
248  }
249  exit(EXIT_FAILURE);
250 }
251 // vim: set tabstop=2 shiftwidth=2 expandtab:
Uptane::RepositoryType
Definition: tuf.h:20
Repo
Definition: repo.h:34
Uptane::Role
TUF Roles.
Definition: tuf.h:57
UptaneRepo
Definition: uptane_repo.h:7
Delegation
Definition: repo.h:19