1 #include <boost/program_options.hpp>
9 #include "logging/logging.h"
10 #include "uptane_repo.h"
12 namespace po = boost::program_options;
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 +
"\""};
18 key_type_str >> key_type;
22 int main(
int argc,
char **argv) {
23 po::options_description desc(
"uptane-generator command line 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");
61 po::positional_options_description positionalOptions;
62 positionalOptions.add(
"command", 1);
63 positionalOptions.add(
"path", 1);
64 positionalOptions.add(
"filename", 1);
68 logger_set_threshold(boost::log::trivial::info);
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);
74 if (vm.count(
"help") != 0) {
75 std::cout << desc << std::endl;
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>();
84 std::string correlation_id;
85 if (vm.count(
"correlationid") != 0) {
86 correlation_id = vm[
"correlationid"].as<std::string>();
89 if (vm.count(
"dname") != 0) {
90 dname = vm[
"dname"].as<std::string>();
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";
104 if (vm.count(
"hwid") == 0) {
105 std::cerr <<
"image command requires --hwid\n";
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>();
113 if (vm.count(
"dname") != 0) {
115 if (!delegation.isMatched(targetname)) {
116 std::cerr <<
"Image path doesn't match delegation!\n";
119 std::cout <<
"Added a target " << targetname <<
" to a delegated role " << dname << std::endl;
122 if (vm.count(
"url") != 0) {
123 url = vm[
"url"].as<std::string>();
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;
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";
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>());
138 hash = std_::make_unique<Uptane::Hash>(Uptane::Hash::Type::kSha512, vm[
"targetsha512"].as<std::string>());
141 if (vm.count(
"targetcustom") > 0 && vm.count(
"targetformat") > 0) {
142 std::cerr <<
"--targetcustom and --targetformat cannot be used together";
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>();
152 repo.addCustomImage(targetname.string(), *hash, vm[
"targetlength"].as<uint64_t>(), hwid, url, delegation,
154 std::cout <<
"Added a custom image target " << targetname.string() << std::endl;
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";
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>();
165 if (vm.count(
"url") != 0) {
166 url = vm[
"url"].as<std::string>();
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";
176 std::string dparent = vm[
"dparent"].as<std::string>();
177 std::string dpattern = vm[
"dpattern"].as<std::string>();
178 KeyType key_type = parseKeyType(vm);
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";
189 std::cout <<
"Revoked the delegation " << dname << std::endl;
190 }
else if (command ==
"signtargets") {
192 std::cout <<
"Signed the staged Director Targets metadata" << std::endl;
193 }
else if (command ==
"emptytargets") {
195 std::cout <<
"Cleared the staged Director Targets metadata" << std::endl;
196 }
else if (command ==
"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";
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);
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";
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";
231 std::cout << desc << std::endl;
235 std::cout << desc << std::endl;
241 }
catch (
const po::error &o) {
242 std::cout << o.what() << std::endl;
244 }
catch (std::exception &ex) {
245 LOG_ERROR <<
"Exception: " << ex.what();
247 LOG_ERROR <<
"Unknown error";